From 01f88de15edd4d8b9f4b2f841935ed2e0915a106 Mon Sep 17 00:00:00 2001 From: ccXxx1aoBai <1426169428@qq.com> Date: Wed, 29 May 2024 18:03:06 +0800 Subject: [PATCH 01/11] =?UTF-8?q?=E6=96=B0=E5=A2=9ETDNoticeBar=E7=BB=84?= =?UTF-8?q?=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tdesign-component/demo_tool/all_build.sh | 1 + tdesign-component/example/lib/config.dart | 3 + .../example/lib/page/td_notice_bar_page.dart | 103 +++++++++ .../components/notice_bar/td_notice_bar.dart | 217 ++++++++++++++++++ tdesign-component/lib/tdesign_flutter.dart | 1 + 5 files changed, 325 insertions(+) create mode 100644 tdesign-component/example/lib/page/td_notice_bar_page.dart create mode 100644 tdesign-component/lib/src/components/notice_bar/td_notice_bar.dart diff --git a/tdesign-component/demo_tool/all_build.sh b/tdesign-component/demo_tool/all_build.sh index de05c73d8..0a2367901 100644 --- a/tdesign-component/demo_tool/all_build.sh +++ b/tdesign-component/demo_tool/all_build.sh @@ -92,6 +92,7 @@ ./bin/demo_tool generate --file ../lib/src/components/loading/td_loading.dart --name TDLoading --folder-name loading --output ../example/assets/api/ --only-api # message # noticeBar +./bin/demo_tool generate --file ./lib/src/components/notice_bar/td_notice_bar.dart --name TDNoticeBar --folder-name notice-bar --output ../example/assets/api/ --only-api --get-comments # overlay # popup ./bin/demo_tool generate --folder ../lib/src/components/popup --name TDSlidePopupRoute,TDPopupBottomDisplayPanel,TDPopupBottomConfirmPanel,TDPopupCenterPanel --folder-name popup --output ../example/assets/api/ --only-api --get-comments diff --git a/tdesign-component/example/lib/config.dart b/tdesign-component/example/lib/config.dart index 0f52c8843..053bda9e4 100644 --- a/tdesign-component/example/lib/config.dart +++ b/tdesign-component/example/lib/config.dart @@ -25,6 +25,7 @@ import 'page/td_input_page.dart'; import 'page/td_link_page.dart'; import 'page/td_loading_page.dart'; import 'page/td_navbar_page.dart'; +import 'page/td_notice_bar_page.dart'; import 'page/td_picker_page.dart'; import 'page/td_popup_page.dart'; import 'page/td_radio_page.dart'; @@ -213,6 +214,8 @@ Map> exampleMap = { pageBuilder: _wrapInheritedTheme((context) => const TDDropdownMenuPage())), ExamplePageModel( text: 'Loading 加载', name: 'loading', pageBuilder: _wrapInheritedTheme((context) => const TDLoadingPage())), + ExamplePageModel( + text: 'NoticeBar 滚动通知', name: 'notice-bar', pageBuilder: _wrapInheritedTheme((context) => const TDNoticeBarPage())), ExamplePageModel( text: 'Message 消息通知', name: 'message', diff --git a/tdesign-component/example/lib/page/td_notice_bar_page.dart b/tdesign-component/example/lib/page/td_notice_bar_page.dart new file mode 100644 index 000000000..c8fb47cbe --- /dev/null +++ b/tdesign-component/example/lib/page/td_notice_bar_page.dart @@ -0,0 +1,103 @@ +/// @Type Flutter +/// @Author lwb +/// @Date 2024/5/28 + +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import '../annotation/demo.dart'; +import '../base/example_widget.dart'; + +class TDNoticeBarPage extends StatelessWidget { + const TDNoticeBarPage({super.key}); + + @override + Widget build(BuildContext context) { + return ExamplePage( + title: tdTitle(context), + exampleCodeGroup: 'noticeBar', + desc: '用于反馈或提示。', + children: [ + ExampleModule(title: '组件类型', children: [ + ExampleItem(desc: '纯文字通知', builder: _textNoticeBar), + ExampleItem(desc: '滚动通知', builder: _scrollNoticeBar), + ExampleItem(desc: '步进滚动通知', builder: _stepNoticeBar), + ExampleItem(desc: '带图标滚动通知', builder: _scrollIconNoticeBar), + ExampleItem(desc: '带图标步进通知', builder: _stepIconNoticeBar), + ]), + ExampleModule(title: '组件样式', children: [ + ExampleItem(desc: '背景色', builder: _setBgColorNoticeBar), + ExampleItem(desc: '文字大小', builder: _setFontSizeNoticeBar), + ]) + ], + ); + } +} + +@Demo(group: 'noticeBar') +Widget _textNoticeBar(BuildContext context) { + return const TDNoticeBar(text: '这是静止的通知内容'); +} + +@Demo(group: 'noticeBar') +Widget _scrollNoticeBar(BuildContext context) { + return const TDNoticeBar( + text: '这是一条滚动通知', + type: TdNoticeBarType.scroll, + duration: 2000, + ); +} + +@Demo(group: 'noticeBar') +Widget _stepNoticeBar(BuildContext context) { + return const TDNoticeBar( + textList: ['这是第一条通知', '这是第二条通知', '这是第三条通知'], + type: TdNoticeBarType.step, + stepDuration: 2000, + duration: 1000, + ); +} + +@Demo(group: 'noticeBar') +Widget _scrollIconNoticeBar(BuildContext context) { + return TDNoticeBar( + text: '这是一条滚动通知', + duration: 2000, + type: TdNoticeBarType.scroll, + left: Icon(TDIcons.sound, color: TDTheme.of(context).brandNormalColor), + right: Icon(TDIcons.chevron_right, color: TDTheme.of(context).grayColor8), + ); +} + +@Demo(group: 'noticeBar') +Widget _stepIconNoticeBar(BuildContext context) { + return TDNoticeBar( + textList: const ['这是第一条通知', '这是第二条通知', '这是第三条通知'], + type: TdNoticeBarType.step, + stepDuration: 2000, + duration: 1000, + left: Icon(TDIcons.sound, color: TDTheme.of(context).brandNormalColor), + right: Icon(TDIcons.chevron_right, color: TDTheme.of(context).grayColor8), + ); +} + +@Demo(group: 'noticeBar') +Widget _setBgColorNoticeBar(BuildContext context) { + return TDNoticeBar( + text: '这是一条滚动通知', + textStyle: const TextStyle(color: Colors.white), + duration: 2000, + type: TdNoticeBarType.scroll, + backgroundColor: TDTheme.of(context).brandNormalColor, + ); +} + +@Demo(group: 'noticeBar') +Widget _setFontSizeNoticeBar(BuildContext context) { + return const TDNoticeBar( + text: '这是一条滚动通知', + textStyle: TextStyle(fontSize: 24), + duration: 3000, + type: TdNoticeBarType.scroll, + ); +} diff --git a/tdesign-component/lib/src/components/notice_bar/td_notice_bar.dart b/tdesign-component/lib/src/components/notice_bar/td_notice_bar.dart new file mode 100644 index 000000000..5246e9f00 --- /dev/null +++ b/tdesign-component/lib/src/components/notice_bar/td_notice_bar.dart @@ -0,0 +1,217 @@ +import 'dart:async'; + +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; + +import '../icon/td_icons.dart'; + +enum TdNoticeBarType { + none, + scroll, + step, +} + +class TDNoticeBar extends StatefulWidget { + const TDNoticeBar({ + super.key, + this.text, + this.textList, + this.textStyle, + this.left, + this.right, + this.backgroundColor = Colors.white, + this.padding = const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + this.duration = 2000, + this.stepDuration = 2000, + this.type = TdNoticeBarType.none, + }); + + /// 显示内容 + final String? text; + + /// 步进滚动内容 + final List? textList; + + /// 文字样式 + final TextStyle? textStyle; + + /// 左侧内容 + final Widget? left; + + /// 右侧内容 + final Widget? right; + + /// 背景色 + final Color? backgroundColor; + + /// 内边距 + final EdgeInsetsGeometry? padding; + + /// 滚动周期 + final int? duration; + + /// 步进滚动停留时间 + final int? stepDuration; + + /// 滚动类型 + final TdNoticeBarType? type; + + @override + State createState() => _TDNoticeBarState(); +} + +class _TDNoticeBarState extends State { + late ScrollController _scrollController; + final GlobalKey _key = GlobalKey(); + Timer? _timer; + late Size _size; + + @override + void initState() { + super.initState(); + if (widget.duration! <= 0) { + throw Exception('duration must not be less than 0'); + } + if (widget.stepDuration! <= 0) { + throw Exception('stepDuration must not be less than 0'); + } + _scrollController = ScrollController(); + WidgetsBinding.instance.addPostFrameCallback((time) { + _startTimer(); + }); + } + + @override + void dispose() { + super.dispose(); + _timer?.cancel(); + _scrollController.dispose(); + } + + void _startTimer() { + if (widget.type == TdNoticeBarType.scroll) { + _scrollScroll(); + } else if (widget.type == TdNoticeBarType.step) { + _stepScroll(); + } + } + + void _scrollScroll() { + if (widget.text == null) { + throw Exception('text must not be null when type is scroll'); + } + _scrollController.jumpTo(0); + _scrollController.animateTo(_size.width, + duration: Duration(milliseconds: widget.duration!), + curve: Curves.linear); + _timer = + Timer.periodic(Duration(milliseconds: widget.duration!), (Timer timer) { + _scrollController.jumpTo(0); + _scrollController.animateTo(_size.width, + duration: Duration(milliseconds: widget.duration!), + curve: Curves.linear); + }); + } + + Future _stepScroll() async { + if (widget.textList == null) { + throw Exception('textList must not be null when type is step'); + } + var index = 0; + var offset = 0.0; + _scrollController.jumpTo(0); + await Future.delayed(Duration(milliseconds: widget.stepDuration!), () { + offset += _size.width; + _scrollController.animateTo(offset, + duration: Duration(milliseconds: widget.duration!), + curve: Curves.linear); + index++; + }); + _timer = Timer.periodic( + Duration(milliseconds: widget.stepDuration! + widget.duration!), + (Timer timer) { + if (index > widget.textList!.length - 1) { + index = 0; + offset = 0; + _scrollController.jumpTo(0); + } + offset += _size.width; + index++; + _scrollController.animateTo(offset, + duration: Duration(milliseconds: widget.duration!), + curve: Curves.linear); + }); + } + + Widget _children() { + if (widget.textList != null) { + return Row( + children: [ + for (var i = 0; i < widget.textList!.length; i++) + SizedBox( + width: _size.width, + child: Text( + widget.textList![i], + style: widget.textStyle, + ), + ), + SizedBox(width: _size.width, child: Text(widget.textList![0])) + ], + ); + } + return Row( + children: [ + SizedBox( + width: _size.width, + child: Text( + widget.text!, + style: widget.textStyle, + ), + ), + SizedBox( + width: _size.width, + child: Text( + widget.text!, + style: widget.textStyle, + ), + ) + ], + ); + } + + @override + Widget build(BuildContext context) { + _size = MediaQuery.of(context).size; + return Container( + padding: widget.padding, + decoration: BoxDecoration( + color: widget.backgroundColor, + ), + child: Row( + children: [ + Visibility( + visible: widget.left != null, + child: Container( + margin: const EdgeInsets.only(right: 8), + child: widget.left, + ), + ), + Expanded( + key: _key, + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + controller: _scrollController, + physics: const NeverScrollableScrollPhysics(), + child: _children())), + Visibility( + visible: widget.right != null, + child: Container( + margin: const EdgeInsets.only(left: 8), + child: widget.right, + ), + ), + ], + ), + ); + } +} diff --git a/tdesign-component/lib/tdesign_flutter.dart b/tdesign-component/lib/tdesign_flutter.dart index daab20a59..15eb34860 100644 --- a/tdesign-component/lib/tdesign_flutter.dart +++ b/tdesign-component/lib/tdesign_flutter.dart @@ -21,6 +21,7 @@ export 'src/components/loading/td_circle_indicator.dart'; export 'src/components/loading/td_loading.dart'; export 'src/components/loading/td_loading_controller.dart'; export 'src/components/navbar/td_nav_bar.dart'; +export 'src/components/notice_bar/td_notice_bar.dart'; export 'src/components/picker/td_date_picker.dart'; export 'src/components/picker/td_item_widget.dart'; export 'src/components/picker/td_multi_picker.dart'; From 89eafd2686162f9504f3e4fe9a48de3ab7eae674 Mon Sep 17 00:00:00 2001 From: ccXxx1aoBai <1426169428@qq.com> Date: Wed, 29 May 2024 18:25:31 +0800 Subject: [PATCH 02/11] =?UTF-8?q?=E7=BB=84=E4=BB=B6=E6=8F=8F=E8=BF=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tdesign-component/example/lib/page/td_notice_bar_page.dart | 2 +- .../lib/src/components/notice_bar/td_notice_bar.dart | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/tdesign-component/example/lib/page/td_notice_bar_page.dart b/tdesign-component/example/lib/page/td_notice_bar_page.dart index c8fb47cbe..95ceca980 100644 --- a/tdesign-component/example/lib/page/td_notice_bar_page.dart +++ b/tdesign-component/example/lib/page/td_notice_bar_page.dart @@ -16,7 +16,7 @@ class TDNoticeBarPage extends StatelessWidget { return ExamplePage( title: tdTitle(context), exampleCodeGroup: 'noticeBar', - desc: '用于反馈或提示。', + desc: '用于警告或提示。', children: [ ExampleModule(title: '组件类型', children: [ ExampleItem(desc: '纯文字通知', builder: _textNoticeBar), diff --git a/tdesign-component/lib/src/components/notice_bar/td_notice_bar.dart b/tdesign-component/lib/src/components/notice_bar/td_notice_bar.dart index 5246e9f00..fa9f5f230 100644 --- a/tdesign-component/lib/src/components/notice_bar/td_notice_bar.dart +++ b/tdesign-component/lib/src/components/notice_bar/td_notice_bar.dart @@ -11,6 +11,7 @@ enum TdNoticeBarType { step, } +/// 用于警告或提示 class TDNoticeBar extends StatefulWidget { const TDNoticeBar({ super.key, From 074a5c32b24552b302c0c1b2fdfafb9eefda7b68 Mon Sep 17 00:00:00 2001 From: ccXxx1aoBai <1426169428@qq.com> Date: Thu, 30 May 2024 11:32:38 +0800 Subject: [PATCH 03/11] =?UTF-8?q?=E7=BB=84=E4=BB=B6=E5=B1=9E=E6=80=A7?= =?UTF-8?q?=E3=80=81=E9=BB=98=E8=AE=A4=E5=80=BC=E4=BF=AE=E6=94=B9=EF=BC=8C?= =?UTF-8?q?=E6=96=B0=E5=A2=9EAPI=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../example/assets/api/notice-bar_api.md | 17 +++++++++++++++++ .../example/lib/page/td_notice_bar_page.dart | 4 ++-- .../components/notice_bar/td_notice_bar.dart | 18 +++++++++--------- 3 files changed, 28 insertions(+), 11 deletions(-) create mode 100644 tdesign-component/example/assets/api/notice-bar_api.md diff --git a/tdesign-component/example/assets/api/notice-bar_api.md b/tdesign-component/example/assets/api/notice-bar_api.md new file mode 100644 index 000000000..90339eb03 --- /dev/null +++ b/tdesign-component/example/assets/api/notice-bar_api.md @@ -0,0 +1,17 @@ +## API +### TDNoticeBar +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +|------------------|---------------------|---------------------------------------------------|--------------| +| key | | - | | +| text | String? | - | 显示内容 | +| textList | List? | - | 步进滚动内容 | +| textStyle | TextStyle? | - | 文字样式 | +| left | Widget? | - | 左侧内容 | +| right | Widget? | - | 右侧内容 | +| backgroundColor | Color? | Colors.white | 背景色 | +| padding | EdgeInsetsGeometry? | EdgeInsets.symmetric(horizontal: 16, vertical: 8) | 内边距 | +| duration | int? | 3000 | 滚动周期(毫秒) | +| interval | int? | 2000 | 步进滚动间隔时间(毫秒) | +| type | TdNoticeBarType? | TdNoticeBarType.scroll | 滚动类型 | diff --git a/tdesign-component/example/lib/page/td_notice_bar_page.dart b/tdesign-component/example/lib/page/td_notice_bar_page.dart index 95ceca980..a716c74d5 100644 --- a/tdesign-component/example/lib/page/td_notice_bar_page.dart +++ b/tdesign-component/example/lib/page/td_notice_bar_page.dart @@ -53,7 +53,7 @@ Widget _stepNoticeBar(BuildContext context) { return const TDNoticeBar( textList: ['这是第一条通知', '这是第二条通知', '这是第三条通知'], type: TdNoticeBarType.step, - stepDuration: 2000, + interval: 2000, duration: 1000, ); } @@ -74,7 +74,7 @@ Widget _stepIconNoticeBar(BuildContext context) { return TDNoticeBar( textList: const ['这是第一条通知', '这是第二条通知', '这是第三条通知'], type: TdNoticeBarType.step, - stepDuration: 2000, + interval: 2000, duration: 1000, left: Icon(TDIcons.sound, color: TDTheme.of(context).brandNormalColor), right: Icon(TDIcons.chevron_right, color: TDTheme.of(context).grayColor8), diff --git a/tdesign-component/lib/src/components/notice_bar/td_notice_bar.dart b/tdesign-component/lib/src/components/notice_bar/td_notice_bar.dart index fa9f5f230..87410b4fe 100644 --- a/tdesign-component/lib/src/components/notice_bar/td_notice_bar.dart +++ b/tdesign-component/lib/src/components/notice_bar/td_notice_bar.dart @@ -22,9 +22,9 @@ class TDNoticeBar extends StatefulWidget { this.right, this.backgroundColor = Colors.white, this.padding = const EdgeInsets.symmetric(horizontal: 16, vertical: 8), - this.duration = 2000, - this.stepDuration = 2000, - this.type = TdNoticeBarType.none, + this.duration = 3000, + this.interval = 2000, + this.type = TdNoticeBarType.scroll, }); /// 显示内容 @@ -48,11 +48,11 @@ class TDNoticeBar extends StatefulWidget { /// 内边距 final EdgeInsetsGeometry? padding; - /// 滚动周期 + /// 滚动周期(毫秒) final int? duration; - /// 步进滚动停留时间 - final int? stepDuration; + /// 步进滚动间隔时间(毫秒) + final int? interval; /// 滚动类型 final TdNoticeBarType? type; @@ -73,7 +73,7 @@ class _TDNoticeBarState extends State { if (widget.duration! <= 0) { throw Exception('duration must not be less than 0'); } - if (widget.stepDuration! <= 0) { + if (widget.interval! <= 0) { throw Exception('stepDuration must not be less than 0'); } _scrollController = ScrollController(); @@ -121,7 +121,7 @@ class _TDNoticeBarState extends State { var index = 0; var offset = 0.0; _scrollController.jumpTo(0); - await Future.delayed(Duration(milliseconds: widget.stepDuration!), () { + await Future.delayed(Duration(milliseconds: widget.interval!), () { offset += _size.width; _scrollController.animateTo(offset, duration: Duration(milliseconds: widget.duration!), @@ -129,7 +129,7 @@ class _TDNoticeBarState extends State { index++; }); _timer = Timer.periodic( - Duration(milliseconds: widget.stepDuration! + widget.duration!), + Duration(milliseconds: widget.interval! + widget.duration!), (Timer timer) { if (index > widget.textList!.length - 1) { index = 0; From bedbdfff8b25abf77233015a7253907c5d6db237 Mon Sep 17 00:00:00 2001 From: ccXxx1aoBai <1426169428@qq.com> Date: Thu, 30 May 2024 11:35:31 +0800 Subject: [PATCH 04/11] =?UTF-8?q?=E7=A4=BA=E4=BE=8B=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tdesign-component/example/lib/page/td_notice_bar_page.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tdesign-component/example/lib/page/td_notice_bar_page.dart b/tdesign-component/example/lib/page/td_notice_bar_page.dart index a716c74d5..faf23ea63 100644 --- a/tdesign-component/example/lib/page/td_notice_bar_page.dart +++ b/tdesign-component/example/lib/page/td_notice_bar_page.dart @@ -36,7 +36,7 @@ class TDNoticeBarPage extends StatelessWidget { @Demo(group: 'noticeBar') Widget _textNoticeBar(BuildContext context) { - return const TDNoticeBar(text: '这是静止的通知内容'); + return const TDNoticeBar(text: '这是静止的通知内容', type: TdNoticeBarType.none); } @Demo(group: 'noticeBar') From 50f64eda7c48dc3a34e4b59a9246d02516eaa3e1 Mon Sep 17 00:00:00 2001 From: ccXxx1aoBai <1426169428@qq.com> Date: Thu, 30 May 2024 11:57:22 +0800 Subject: [PATCH 05/11] =?UTF-8?q?=E7=A4=BA=E4=BE=8B=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tdesign-component/example/lib/config.dart | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/tdesign-component/example/lib/config.dart b/tdesign-component/example/lib/config.dart index 053bda9e4..23f9300cb 100644 --- a/tdesign-component/example/lib/config.dart +++ b/tdesign-component/example/lib/config.dart @@ -214,18 +214,13 @@ Map> exampleMap = { pageBuilder: _wrapInheritedTheme((context) => const TDDropdownMenuPage())), ExamplePageModel( text: 'Loading 加载', name: 'loading', pageBuilder: _wrapInheritedTheme((context) => const TDLoadingPage())), - ExamplePageModel( - text: 'NoticeBar 滚动通知', name: 'notice-bar', pageBuilder: _wrapInheritedTheme((context) => const TDNoticeBarPage())), ExamplePageModel( text: 'Message 消息通知', name: 'message', isTodo: true, pageBuilder: _wrapInheritedTheme((context) => const TodoPage())), ExamplePageModel( - text: 'NoticeBar 公告栏', - name: 'notice_bar', - isTodo: true, - pageBuilder: _wrapInheritedTheme((context) => const TodoPage())), + text: 'NoticeBar 公告栏', name: 'notice-bar', pageBuilder: _wrapInheritedTheme((context) => const TDNoticeBarPage())), ExamplePageModel( text: 'Overlay 遮罩层', name: 'overlay', From 610114c9c508aff103eb179a76a872178c8c48bd Mon Sep 17 00:00:00 2001 From: ccXxx1aoBai <1426169428@qq.com> Date: Thu, 30 May 2024 17:02:37 +0800 Subject: [PATCH 06/11] =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=9E=82=E7=9B=B4?= =?UTF-8?q?=E6=BB=9A=E5=8A=A8=E3=80=81=E4=BF=AE=E6=94=B9=E5=B1=9E=E6=80=A7?= =?UTF-8?q?=E5=90=8D=E7=A7=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../example/assets/api/notice-bar_api.md | 7 +- .../example/lib/page/td_notice_bar_page.dart | 28 +++-- .../components/notice_bar/td_notice_bar.dart | 113 +++++++++++------- 3 files changed, 95 insertions(+), 53 deletions(-) diff --git a/tdesign-component/example/assets/api/notice-bar_api.md b/tdesign-component/example/assets/api/notice-bar_api.md index 90339eb03..4cceda535 100644 --- a/tdesign-component/example/assets/api/notice-bar_api.md +++ b/tdesign-component/example/assets/api/notice-bar_api.md @@ -5,13 +5,14 @@ | 参数 | 类型 | 默认值 | 说明 | |------------------|---------------------|---------------------------------------------------|--------------| | key | | - | | -| text | String? | - | 显示内容 | -| textList | List? | - | 步进滚动内容 | +| context | String? | - | 文本内容 | +| contexts | List? | - | 步进滚动内容 | | textStyle | TextStyle? | - | 文字样式 | | left | Widget? | - | 左侧内容 | | right | Widget? | - | 右侧内容 | | backgroundColor | Color? | Colors.white | 背景色 | -| padding | EdgeInsetsGeometry? | EdgeInsets.symmetric(horizontal: 16, vertical: 8) | 内边距 | +| padding | EdgeInsetsGeometry? | const EdgeInsets.symmetric(horizontal: 16, vertical: 8) | 内边距 | | duration | int? | 3000 | 滚动周期(毫秒) | | interval | int? | 2000 | 步进滚动间隔时间(毫秒) | | type | TdNoticeBarType? | TdNoticeBarType.scroll | 滚动类型 | +| direction | Axis? | Axis.horizontal | 滚动方向(步进可选) | diff --git a/tdesign-component/example/lib/page/td_notice_bar_page.dart b/tdesign-component/example/lib/page/td_notice_bar_page.dart index faf23ea63..a7d4ccbdf 100644 --- a/tdesign-component/example/lib/page/td_notice_bar_page.dart +++ b/tdesign-component/example/lib/page/td_notice_bar_page.dart @@ -24,6 +24,7 @@ class TDNoticeBarPage extends StatelessWidget { ExampleItem(desc: '步进滚动通知', builder: _stepNoticeBar), ExampleItem(desc: '带图标滚动通知', builder: _scrollIconNoticeBar), ExampleItem(desc: '带图标步进通知', builder: _stepIconNoticeBar), + ExampleItem(desc: '带图标垂直步进通知', builder: _stepVerticalIconNoticeBar), ]), ExampleModule(title: '组件样式', children: [ ExampleItem(desc: '背景色', builder: _setBgColorNoticeBar), @@ -36,13 +37,13 @@ class TDNoticeBarPage extends StatelessWidget { @Demo(group: 'noticeBar') Widget _textNoticeBar(BuildContext context) { - return const TDNoticeBar(text: '这是静止的通知内容', type: TdNoticeBarType.none); + return const TDNoticeBar(context: '这是静止的通知内容', type: TdNoticeBarType.none); } @Demo(group: 'noticeBar') Widget _scrollNoticeBar(BuildContext context) { return const TDNoticeBar( - text: '这是一条滚动通知', + context: '这是一条滚动通知', type: TdNoticeBarType.scroll, duration: 2000, ); @@ -51,7 +52,7 @@ Widget _scrollNoticeBar(BuildContext context) { @Demo(group: 'noticeBar') Widget _stepNoticeBar(BuildContext context) { return const TDNoticeBar( - textList: ['这是第一条通知', '这是第二条通知', '这是第三条通知'], + contexts: ['这是第一条通知', '这是第二条通知', '这是第三条通知'], type: TdNoticeBarType.step, interval: 2000, duration: 1000, @@ -61,7 +62,7 @@ Widget _stepNoticeBar(BuildContext context) { @Demo(group: 'noticeBar') Widget _scrollIconNoticeBar(BuildContext context) { return TDNoticeBar( - text: '这是一条滚动通知', + context: '这是一条滚动通知', duration: 2000, type: TdNoticeBarType.scroll, left: Icon(TDIcons.sound, color: TDTheme.of(context).brandNormalColor), @@ -72,7 +73,7 @@ Widget _scrollIconNoticeBar(BuildContext context) { @Demo(group: 'noticeBar') Widget _stepIconNoticeBar(BuildContext context) { return TDNoticeBar( - textList: const ['这是第一条通知', '这是第二条通知', '这是第三条通知'], + contexts: const ['这是第一条通知', '这是第二条通知', '这是第三条通知'], type: TdNoticeBarType.step, interval: 2000, duration: 1000, @@ -81,10 +82,23 @@ Widget _stepIconNoticeBar(BuildContext context) { ); } +@Demo(group: 'noticeBar') +Widget _stepVerticalIconNoticeBar(BuildContext context) { + return TDNoticeBar( + contexts: const ['这是第一条通知', '这是第二条通知', '这是第三条通知'], + type: TdNoticeBarType.step, + interval: 2000, + duration: 300, + direction: Axis.vertical, + left: Icon(TDIcons.sound, color: TDTheme.of(context).brandNormalColor), + right: Icon(TDIcons.chevron_right, color: TDTheme.of(context).grayColor8), + ); +} + @Demo(group: 'noticeBar') Widget _setBgColorNoticeBar(BuildContext context) { return TDNoticeBar( - text: '这是一条滚动通知', + context: '这是一条滚动通知', textStyle: const TextStyle(color: Colors.white), duration: 2000, type: TdNoticeBarType.scroll, @@ -95,7 +109,7 @@ Widget _setBgColorNoticeBar(BuildContext context) { @Demo(group: 'noticeBar') Widget _setFontSizeNoticeBar(BuildContext context) { return const TDNoticeBar( - text: '这是一条滚动通知', + context: '这是一条滚动通知', textStyle: TextStyle(fontSize: 24), duration: 3000, type: TdNoticeBarType.scroll, diff --git a/tdesign-component/lib/src/components/notice_bar/td_notice_bar.dart b/tdesign-component/lib/src/components/notice_bar/td_notice_bar.dart index 87410b4fe..2df6ddc95 100644 --- a/tdesign-component/lib/src/components/notice_bar/td_notice_bar.dart +++ b/tdesign-component/lib/src/components/notice_bar/td_notice_bar.dart @@ -15,8 +15,8 @@ enum TdNoticeBarType { class TDNoticeBar extends StatefulWidget { const TDNoticeBar({ super.key, - this.text, - this.textList, + this.context, + this.contexts, this.textStyle, this.left, this.right, @@ -25,13 +25,14 @@ class TDNoticeBar extends StatefulWidget { this.duration = 3000, this.interval = 2000, this.type = TdNoticeBarType.scroll, + this.direction = Axis.horizontal, }); - /// 显示内容 - final String? text; + /// 文本内容 + final String? context; /// 步进滚动内容 - final List? textList; + final List? contexts; /// 文字样式 final TextStyle? textStyle; @@ -57,6 +58,9 @@ class TDNoticeBar extends StatefulWidget { /// 滚动类型 final TdNoticeBarType? type; + /// 滚动方向(步进可选) + final Axis? direction; + @override State createState() => _TDNoticeBarState(); } @@ -98,9 +102,12 @@ class _TDNoticeBarState extends State { } void _scrollScroll() { - if (widget.text == null) { + if (widget.context == null) { throw Exception('text must not be null when type is scroll'); } + if (widget.direction == Axis.vertical) { + throw Exception('direction must be horizontal when type is scroll'); + } _scrollController.jumpTo(0); _scrollController.animateTo(_size.width, duration: Duration(milliseconds: widget.duration!), @@ -115,14 +122,16 @@ class _TDNoticeBarState extends State { } Future _stepScroll() async { - if (widget.textList == null) { + if (widget.contexts == null || widget.contexts!.isEmpty) { throw Exception('textList must not be null when type is step'); } var index = 0; var offset = 0.0; + var scrollDistance = + widget.direction == Axis.horizontal ? _size.width : _getFontSize(); _scrollController.jumpTo(0); await Future.delayed(Duration(milliseconds: widget.interval!), () { - offset += _size.width; + offset += scrollDistance; _scrollController.animateTo(offset, duration: Duration(milliseconds: widget.duration!), curve: Curves.linear); @@ -131,12 +140,12 @@ class _TDNoticeBarState extends State { _timer = Timer.periodic( Duration(milliseconds: widget.interval! + widget.duration!), (Timer timer) { - if (index > widget.textList!.length - 1) { + if (index > widget.contexts!.length - 1) { index = 0; offset = 0; _scrollController.jumpTo(0); } - offset += _size.width; + offset += scrollDistance; index++; _scrollController.animateTo(offset, duration: Duration(milliseconds: widget.duration!), @@ -144,46 +153,64 @@ class _TDNoticeBarState extends State { }); } - Widget _children() { - if (widget.textList != null) { - return Row( - children: [ - for (var i = 0; i < widget.textList!.length; i++) - SizedBox( - width: _size.width, - child: Text( - widget.textList![i], - style: widget.textStyle, - ), - ), - SizedBox(width: _size.width, child: Text(widget.textList![0])) - ], - ); + Widget _buildWidget() { + if (widget.direction == Axis.horizontal) { + return Row(children: _children()); } - return Row( - children: [ - SizedBox( - width: _size.width, - child: Text( - widget.text!, - style: widget.textStyle, + return Column(children: _children()); + } + + List _children() { + if (widget.contexts != null) { + var items = []; + for (var i = 0; i < widget.contexts!.length; i++) { + items.add( + SizedBox( + width: _size.width, + child: Text( + widget.contexts![i], + style: widget.textStyle, + ), ), + ); + } + items.add(SizedBox(width: _size.width, child: Text(widget.contexts![0]))); + return items; + } + return [ + SizedBox( + width: _size.width, + child: Text( + widget.context!, + style: widget.textStyle, ), - SizedBox( - width: _size.width, - child: Text( - widget.text!, - style: widget.textStyle, - ), - ) - ], - ); + ), + SizedBox( + width: _size.width, + child: Text( + widget.context!, + style: widget.textStyle, + ), + ), + ]; + } + + double _getFontSize() { + final textPainter = TextPainter( + text: TextSpan( + text: widget.context ?? widget.contexts![0], + style: widget.textStyle, + ), + textDirection: TextDirection.ltr, + )..layout(maxWidth: _size.width); + return textPainter.size.height; } @override Widget build(BuildContext context) { _size = MediaQuery.of(context).size; return Container( + height: _getFontSize() + widget.padding!.vertical, padding: widget.padding, decoration: BoxDecoration( color: widget.backgroundColor, @@ -200,10 +227,10 @@ class _TDNoticeBarState extends State { Expanded( key: _key, child: SingleChildScrollView( - scrollDirection: Axis.horizontal, + scrollDirection: widget.direction!, controller: _scrollController, physics: const NeverScrollableScrollPhysics(), - child: _children())), + child: _buildWidget())), Visibility( visible: widget.right != null, child: Container( From 0e574432e311ed0283f074a0877f92d47b168001 Mon Sep 17 00:00:00 2001 From: ccXxx1aoBai <1426169428@qq.com> Date: Fri, 7 Jun 2024 18:13:50 +0800 Subject: [PATCH 07/11] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=BB=84=E4=BB=B6?= =?UTF-8?q?=E6=A0=B7=E5=BC=8F=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../example/lib/page/td_notice_bar_page.dart | 67 ++-- .../components/notice_bar/td_notice_bar.dart | 291 +++++++++--------- .../notice_bar/td_notice_bar_style.dart | 93 ++++++ tdesign-component/lib/tdesign_flutter.dart | 1 + 4 files changed, 271 insertions(+), 181 deletions(-) create mode 100644 tdesign-component/lib/src/components/notice_bar/td_notice_bar_style.dart diff --git a/tdesign-component/example/lib/page/td_notice_bar_page.dart b/tdesign-component/example/lib/page/td_notice_bar_page.dart index a7d4ccbdf..334e12b67 100644 --- a/tdesign-component/example/lib/page/td_notice_bar_page.dart +++ b/tdesign-component/example/lib/page/td_notice_bar_page.dart @@ -16,19 +16,19 @@ class TDNoticeBarPage extends StatelessWidget { return ExamplePage( title: tdTitle(context), exampleCodeGroup: 'noticeBar', - desc: '用于警告或提示。', + desc: '在导航栏下方,用于给用户显示提示消息。', children: [ ExampleModule(title: '组件类型', children: [ - ExampleItem(desc: '纯文字通知', builder: _textNoticeBar), - ExampleItem(desc: '滚动通知', builder: _scrollNoticeBar), - ExampleItem(desc: '步进滚动通知', builder: _stepNoticeBar), - ExampleItem(desc: '带图标滚动通知', builder: _scrollIconNoticeBar), - ExampleItem(desc: '带图标步进通知', builder: _stepIconNoticeBar), - ExampleItem(desc: '带图标垂直步进通知', builder: _stepVerticalIconNoticeBar), + ExampleItem(desc: '纯文字的公告栏', builder: _textNoticeBar), + ExampleItem(desc: '可滚动的公告栏', builder: _scrollNoticeBar), + // ExampleItem(builder: _scrollIconNoticeBar), + // ExampleItem(desc: '步进滚动通知', builder: _stepNoticeBar), + // ExampleItem(desc: '带图标步进通知', builder: _stepIconNoticeBar), + // ExampleItem(desc: '带图标垂直步进通知', builder: _stepVerticalIconNoticeBar), ]), ExampleModule(title: '组件样式', children: [ - ExampleItem(desc: '背景色', builder: _setBgColorNoticeBar), - ExampleItem(desc: '文字大小', builder: _setFontSizeNoticeBar), + // ExampleItem(desc: '背景色', builder: _setBgColorNoticeBar), + // ExampleItem(desc: '文字大小', builder: _setFontSizeNoticeBar), ]) ], ); @@ -37,46 +37,45 @@ class TDNoticeBarPage extends StatelessWidget { @Demo(group: 'noticeBar') Widget _textNoticeBar(BuildContext context) { - return const TDNoticeBar(context: '这是静止的通知内容', type: TdNoticeBarType.none); + return const TDNoticeBar(context: '这是一条普通的通知信息'); } @Demo(group: 'noticeBar') Widget _scrollNoticeBar(BuildContext context) { return const TDNoticeBar( - context: '这是一条滚动通知', - type: TdNoticeBarType.scroll, - duration: 2000, + context: '提示文字描述提示文字描述提示文字描述提示文字描述提示文字', + marquee: true, + speed: 50, ); } @Demo(group: 'noticeBar') Widget _stepNoticeBar(BuildContext context) { return const TDNoticeBar( - contexts: ['这是第一条通知', '这是第二条通知', '这是第三条通知'], - type: TdNoticeBarType.step, + context: ['这是第一条通知', '这是第二条通知', '这是第三条通知'], interval: 2000, - duration: 1000, + speed: 50, ); } @Demo(group: 'noticeBar') Widget _scrollIconNoticeBar(BuildContext context) { - return TDNoticeBar( - context: '这是一条滚动通知', - duration: 2000, - type: TdNoticeBarType.scroll, - left: Icon(TDIcons.sound, color: TDTheme.of(context).brandNormalColor), - right: Icon(TDIcons.chevron_right, color: TDTheme.of(context).grayColor8), + return Padding( + padding: const EdgeInsets.only(top: 16), + child: TDNoticeBar( + context: '这是一条滚动通知', + speed: 50, + left: Icon(TDIcons.sound, color: TDTheme.of(context).brandNormalColor), + ), ); } @Demo(group: 'noticeBar') Widget _stepIconNoticeBar(BuildContext context) { return TDNoticeBar( - contexts: const ['这是第一条通知', '这是第二条通知', '这是第三条通知'], - type: TdNoticeBarType.step, + context: const ['这是第一条通知', '这是第二条通知', '这是第三条通知'], interval: 2000, - duration: 1000, + speed: 50, left: Icon(TDIcons.sound, color: TDTheme.of(context).brandNormalColor), right: Icon(TDIcons.chevron_right, color: TDTheme.of(context).grayColor8), ); @@ -85,10 +84,9 @@ Widget _stepIconNoticeBar(BuildContext context) { @Demo(group: 'noticeBar') Widget _stepVerticalIconNoticeBar(BuildContext context) { return TDNoticeBar( - contexts: const ['这是第一条通知', '这是第二条通知', '这是第三条通知'], - type: TdNoticeBarType.step, + context: const ['这是第一条通知', '这是第二条通知', '这是第三条通知'], interval: 2000, - duration: 300, + speed: 300, direction: Axis.vertical, left: Icon(TDIcons.sound, color: TDTheme.of(context).brandNormalColor), right: Icon(TDIcons.chevron_right, color: TDTheme.of(context).grayColor8), @@ -99,19 +97,16 @@ Widget _stepVerticalIconNoticeBar(BuildContext context) { Widget _setBgColorNoticeBar(BuildContext context) { return TDNoticeBar( context: '这是一条滚动通知', - textStyle: const TextStyle(color: Colors.white), - duration: 2000, - type: TdNoticeBarType.scroll, - backgroundColor: TDTheme.of(context).brandNormalColor, + style: TDNoticeBarStyle(textStyle: const TextStyle(color: Colors.white)), + speed: 50, ); } @Demo(group: 'noticeBar') Widget _setFontSizeNoticeBar(BuildContext context) { - return const TDNoticeBar( + return TDNoticeBar( context: '这是一条滚动通知', - textStyle: TextStyle(fontSize: 24), - duration: 3000, - type: TdNoticeBarType.scroll, + style: TDNoticeBarStyle(textStyle: const TextStyle(fontSize: 24)), + speed: 50, ); } diff --git a/tdesign-component/lib/src/components/notice_bar/td_notice_bar.dart b/tdesign-component/lib/src/components/notice_bar/td_notice_bar.dart index 2df6ddc95..8d24e0341 100644 --- a/tdesign-component/lib/src/components/notice_bar/td_notice_bar.dart +++ b/tdesign-component/lib/src/components/notice_bar/td_notice_bar.dart @@ -1,41 +1,33 @@ import 'dart:async'; +import 'dart:convert'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; -import '../icon/td_icons.dart'; - -enum TdNoticeBarType { - none, - scroll, - step, -} +import '../../../tdesign_flutter.dart'; /// 用于警告或提示 class TDNoticeBar extends StatefulWidget { const TDNoticeBar({ super.key, this.context, - this.contexts, - this.textStyle, + this.style, this.left, this.right, - this.backgroundColor = Colors.white, - this.padding = const EdgeInsets.symmetric(horizontal: 16, vertical: 8), - this.duration = 3000, - this.interval = 2000, - this.type = TdNoticeBarType.scroll, + this.speed = 50, + this.interval = 3000, + this.marquee = false, this.direction = Axis.horizontal, + this.theme = TDNoticeBarTheme.info, + this.prefixIcon, + this.suffixIcon, }); /// 文本内容 - final String? context; - - /// 步进滚动内容 - final List? contexts; + final dynamic context; /// 文字样式 - final TextStyle? textStyle; + final TDNoticeBarStyle? style; /// 左侧内容 final Widget? left; @@ -43,46 +35,55 @@ class TDNoticeBar extends StatefulWidget { /// 右侧内容 final Widget? right; - /// 背景色 - final Color? backgroundColor; + /// 跑马灯效果 + final bool? marquee; - /// 内边距 - final EdgeInsetsGeometry? padding; - - /// 滚动周期(毫秒) - final int? duration; + /// 滚动速度 + final double? speed; /// 步进滚动间隔时间(毫秒) final int? interval; - /// 滚动类型 - final TdNoticeBarType? type; - /// 滚动方向(步进可选) final Axis? direction; + /// 内置主题 + final TDNoticeBarTheme? theme; + + /// 前置图标 + final IconData? prefixIcon; + + /// 右侧图标 + final IconData? suffixIcon; + @override State createState() => _TDNoticeBarState(); } class _TDNoticeBarState extends State { - late ScrollController _scrollController; - final GlobalKey _key = GlobalKey(); + ScrollController? _scrollController; Timer? _timer; - late Size _size; + Size? _size; + TDNoticeBarStyle? _style; + Color? _backgroundColor; + Widget? _left; + Widget? _right; @override void initState() { super.initState(); - if (widget.duration! <= 0) { - throw Exception('duration must not be less than 0'); + if (widget.speed! < 0) { + throw Exception('speed must not be less than 0'); } if (widget.interval! <= 0) { throw Exception('stepDuration must not be less than 0'); } _scrollController = ScrollController(); + _init(); WidgetsBinding.instance.addPostFrameCallback((time) { - _startTimer(); + if (widget.marquee == true) { + _startTimer(); + } }); } @@ -90,152 +91,152 @@ class _TDNoticeBarState extends State { void dispose() { super.dispose(); _timer?.cancel(); - _scrollController.dispose(); + _scrollController?.dispose(); } - void _startTimer() { - if (widget.type == TdNoticeBarType.scroll) { - _scrollScroll(); - } else if (widget.type == TdNoticeBarType.step) { - _stepScroll(); + void _init() { + if (widget.style != null) { + _style = widget.style; + } else { + _style = TDNoticeBarStyle.generateTheme(context, theme: widget.theme); } + _backgroundColor = _style!.backgroundColor; + _setLeftWidget(); + _setRightWidget(); } - void _scrollScroll() { - if (widget.context == null) { - throw Exception('text must not be null when type is scroll'); - } - if (widget.direction == Axis.vertical) { - throw Exception('direction must be horizontal when type is scroll'); + void _startTimer() { + if (widget.direction == Axis.horizontal) { + _scroll(); + } else if (widget.direction == Axis.vertical) { + _step(); } - _scrollController.jumpTo(0); - _scrollController.animateTo(_size.width, - duration: Duration(milliseconds: widget.duration!), - curve: Curves.linear); - _timer = - Timer.periodic(Duration(milliseconds: widget.duration!), (Timer timer) { - _scrollController.jumpTo(0); - _scrollController.animateTo(_size.width, - duration: Duration(milliseconds: widget.duration!), - curve: Curves.linear); - }); } - Future _stepScroll() async { - if (widget.contexts == null || widget.contexts!.isEmpty) { - throw Exception('textList must not be null when type is step'); + void _scroll() { + var scrollDistance = _getContextWidth() + _size!.width; + if (_getContextWidth() > _size!.width) { + scrollDistance = _getContextWidth() + _getEmptyWidth(); } - var index = 0; - var offset = 0.0; - var scrollDistance = - widget.direction == Axis.horizontal ? _size.width : _getFontSize(); - _scrollController.jumpTo(0); - await Future.delayed(Duration(milliseconds: widget.interval!), () { - offset += scrollDistance; - _scrollController.animateTo(offset, - duration: Duration(milliseconds: widget.duration!), - curve: Curves.linear); - index++; - }); - _timer = Timer.periodic( - Duration(milliseconds: widget.interval! + widget.duration!), - (Timer timer) { - if (index > widget.contexts!.length - 1) { - index = 0; - offset = 0; - _scrollController.jumpTo(0); + _scrollController!.jumpTo(0); + var offset = 0.0 + widget.speed!; + _scrollController!.animateTo(offset, + duration: const Duration(seconds: 1), curve: Curves.linear); + _timer = Timer.periodic(const Duration(seconds: 1), (timer) async { + offset += widget.speed!; + if (offset >= scrollDistance) { + // 最后一阶段未滚动距离转化时间比(毫秒数) + offset = 50; + _scrollController!.jumpTo(0); } - offset += scrollDistance; - index++; - _scrollController.animateTo(offset, - duration: Duration(milliseconds: widget.duration!), - curve: Curves.linear); + _scrollController!.animateTo(offset, + duration: const Duration(seconds: 1), curve: Curves.linear); }); } - Widget _buildWidget() { - if (widget.direction == Axis.horizontal) { - return Row(children: _children()); + void _step() {} + + double _getFontSize() { + return _style!.getTextStyle.fontSize ?? 16; + } + + void _setLeftWidget() { + if (widget.prefixIcon != null) { + _left = Icon( + widget.prefixIcon, + color: widget.style?.leftIconColor, + ); + } + if (widget.left != null) { + _left = widget.left; } - return Column(children: _children()); } - List _children() { - if (widget.contexts != null) { - var items = []; - for (var i = 0; i < widget.contexts!.length; i++) { - items.add( - SizedBox( - width: _size.width, - child: Text( - widget.contexts![i], - style: widget.textStyle, - ), - ), - ); - } - items.add(SizedBox(width: _size.width, child: Text(widget.contexts![0]))); - return items; + void _setRightWidget() { + if (widget.suffixIcon != null) { + _right = Icon( + widget.suffixIcon, + color: widget.style?.rightIconColor, + ); + } + if (widget.right != null) { + _right = widget.right; } - return [ - SizedBox( - width: _size.width, - child: Text( - widget.context!, - style: widget.textStyle, - ), - ), - SizedBox( - width: _size.width, - child: Text( - widget.context!, - style: widget.textStyle, - ), - ), - ]; } - double _getFontSize() { - final textPainter = TextPainter( - text: TextSpan( - text: widget.context ?? widget.contexts![0], - style: widget.textStyle, - ), - textDirection: TextDirection.ltr, - )..layout(maxWidth: _size.width); - return textPainter.size.height; + double _getContextWidth() { + var contextWidth = _getFontSize() * widget.context!.length; + if (contextWidth > _size!.width) { + return contextWidth; + } + return _size!.width; + } + + double _getEmptyWidth() { + print('32ssss::::::::::${_style!.getPadding}:::${_style!.getPadding.horizontal}'); + return _size!.width - _style!.getPadding.horizontal; + } + + Widget _contextWidget() { + Widget textWidget = TDText(widget.context, style: _style?.getTextStyle); + Widget? child = textWidget; + switch (widget.direction) { + case Axis.horizontal: + child = SingleChildScrollView( + controller: _scrollController, + scrollDirection: widget.direction!, + // physics: const NeverScrollableScrollPhysics(), + child: Row( + children: [ + SizedBox( + width: _getContextWidth(), + height: _getFontSize(), + child: textWidget, + ), + SizedBox(width: _getEmptyWidth()), + SizedBox( + width: _getContextWidth(), + height: _getFontSize(), + child: textWidget, + ) + ], + ), + ); + break; + case Axis.vertical: + break; + default: + child = textWidget; + break; + } + return child; } @override Widget build(BuildContext context) { _size = MediaQuery.of(context).size; return Container( - height: _getFontSize() + widget.padding!.vertical, - padding: widget.padding, + width: _size!.width, + height: _getFontSize() + _style!.getPadding.vertical, + padding: _style!.getPadding, decoration: BoxDecoration( - color: widget.backgroundColor, + color: _backgroundColor, ), child: Row( children: [ Visibility( - visible: widget.left != null, + visible: _left != null, child: Container( - margin: const EdgeInsets.only(right: 8), - child: widget.left, + child: _left, ), ), Expanded( - key: _key, - child: SingleChildScrollView( - scrollDirection: widget.direction!, - controller: _scrollController, - physics: const NeverScrollableScrollPhysics(), - child: _buildWidget())), + child: _contextWidget(), + ), Visibility( - visible: widget.right != null, + visible: _right != null, child: Container( - margin: const EdgeInsets.only(left: 8), - child: widget.right, + child: _right, ), ), ], diff --git a/tdesign-component/lib/src/components/notice_bar/td_notice_bar_style.dart b/tdesign-component/lib/src/components/notice_bar/td_notice_bar_style.dart new file mode 100644 index 000000000..86a09a623 --- /dev/null +++ b/tdesign-component/lib/src/components/notice_bar/td_notice_bar_style.dart @@ -0,0 +1,93 @@ +import 'package:flutter/material.dart'; + +import '../../theme/td_colors.dart'; +import '../../theme/td_theme.dart'; +import 'td_notice_bar.dart'; + +/// 公告栏类型 +enum TDNoticeBarType { + /// 静止(默认) + none, + + /// 滚动 + scroll, + + /// 步进 + step +} + +enum TDNoticeBarTheme { + /// 默认 + info, + + /// 成功 + success, + + /// 警告 + warning, + + /// 错误 + error +} + +/// 公告栏样式 +class TDNoticeBarStyle { + TDNoticeBarStyle( + {this.context, + this.backgroundColor, + this.textStyle, + this.leftIconColor, + this.rightIconColor, + this.padding}); + + /// 上下文 + BuildContext? context; + + /// 公告栏背景色 + Color? backgroundColor; + + /// 公告栏左侧图标颜色 + Color? leftIconColor; + + /// 公告栏右侧图标颜色 + Color? rightIconColor; + + /// 公告栏内边距 + EdgeInsetsGeometry? padding; + + /// 公告栏内容样式 + TextStyle? textStyle; + + /// 公告栏内边距,用于获取默认值 + EdgeInsetsGeometry get getPadding => + padding ?? + const EdgeInsets.only(top: 13, bottom: 13, left: 16, right: 12); + + /// 公告栏内容样式,用于获取默认值 + TextStyle get getTextStyle => + textStyle ?? + TextStyle( + color: TDTheme.of(context).fontGyColor1, fontSize: 16, height: 1); + + TDNoticeBarStyle.generateTheme(BuildContext context, + {TDNoticeBarTheme? theme = TDNoticeBarTheme.info}) { + switch (theme) { + case TDNoticeBarTheme.warning: + leftIconColor = TDTheme.of(context).warningNormalColor; + backgroundColor = TDTheme.of(context).warningLightColor; + break; + case TDNoticeBarTheme.error: + leftIconColor = TDTheme.of(context).errorNormalColor; + backgroundColor = TDTheme.of(context).errorLightColor; + break; + case TDNoticeBarTheme.success: + leftIconColor = TDTheme.of(context).successNormalColor; + backgroundColor = TDTheme.of(context).successLightColor; + break; + default: + leftIconColor = TDTheme.of(context).brandNormalColor; + backgroundColor = TDTheme.of(context).brandLightColor; + break; + } + } +} diff --git a/tdesign-component/lib/tdesign_flutter.dart b/tdesign-component/lib/tdesign_flutter.dart index 15eb34860..7acbe76ed 100644 --- a/tdesign-component/lib/tdesign_flutter.dart +++ b/tdesign-component/lib/tdesign_flutter.dart @@ -22,6 +22,7 @@ export 'src/components/loading/td_loading.dart'; export 'src/components/loading/td_loading_controller.dart'; export 'src/components/navbar/td_nav_bar.dart'; export 'src/components/notice_bar/td_notice_bar.dart'; +export 'src/components/notice_bar/td_notice_bar_style.dart'; export 'src/components/picker/td_date_picker.dart'; export 'src/components/picker/td_item_widget.dart'; export 'src/components/picker/td_multi_picker.dart'; From f03252973a30f1033e331abc05818f5de1638438 Mon Sep 17 00:00:00 2001 From: ccXxx1aoBai <1426169428@qq.com> Date: Sat, 8 Jun 2024 11:18:29 +0800 Subject: [PATCH 08/11] =?UTF-8?q?noticeBar=E7=BB=84=E4=BB=B6=E9=87=8D?= =?UTF-8?q?=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tdesign-component/demo_tool/all_build.sh | 2 +- .../example/assets/api/notice-bar_api.md | 40 +++- .../example/lib/page/td_notice_bar_page.dart | 222 +++++++++++++++--- .../components/notice_bar/td_notice_bar.dart | 218 +++++++++++++---- .../notice_bar/td_notice_bar_style.dart | 3 + 5 files changed, 388 insertions(+), 97 deletions(-) diff --git a/tdesign-component/demo_tool/all_build.sh b/tdesign-component/demo_tool/all_build.sh index 0a2367901..3f5b58095 100644 --- a/tdesign-component/demo_tool/all_build.sh +++ b/tdesign-component/demo_tool/all_build.sh @@ -92,7 +92,7 @@ ./bin/demo_tool generate --file ../lib/src/components/loading/td_loading.dart --name TDLoading --folder-name loading --output ../example/assets/api/ --only-api # message # noticeBar -./bin/demo_tool generate --file ./lib/src/components/notice_bar/td_notice_bar.dart --name TDNoticeBar --folder-name notice-bar --output ../example/assets/api/ --only-api --get-comments +./bin/demo_tool generate --file ../lib/src/components/notice_bar --name TDNoticeBar,TDNoticeBarStyle --folder-name notice-bar --output ../example/assets/api/ --only-api --get-comments # overlay # popup ./bin/demo_tool generate --folder ../lib/src/components/popup --name TDSlidePopupRoute,TDPopupBottomDisplayPanel,TDPopupBottomConfirmPanel,TDPopupCenterPanel --folder-name popup --output ../example/assets/api/ --only-api --get-comments diff --git a/tdesign-component/example/assets/api/notice-bar_api.md b/tdesign-component/example/assets/api/notice-bar_api.md index 4cceda535..e54c1b6f9 100644 --- a/tdesign-component/example/assets/api/notice-bar_api.md +++ b/tdesign-component/example/assets/api/notice-bar_api.md @@ -3,16 +3,32 @@ #### 默认构造方法 | 参数 | 类型 | 默认值 | 说明 | -|------------------|---------------------|---------------------------------------------------|--------------| +| --- | --- | --- | --- | | key | | - | | -| context | String? | - | 文本内容 | -| contexts | List? | - | 步进滚动内容 | -| textStyle | TextStyle? | - | 文字样式 | -| left | Widget? | - | 左侧内容 | -| right | Widget? | - | 右侧内容 | -| backgroundColor | Color? | Colors.white | 背景色 | -| padding | EdgeInsetsGeometry? | const EdgeInsets.symmetric(horizontal: 16, vertical: 8) | 内边距 | -| duration | int? | 3000 | 滚动周期(毫秒) | -| interval | int? | 2000 | 步进滚动间隔时间(毫秒) | -| type | TdNoticeBarType? | TdNoticeBarType.scroll | 滚动类型 | -| direction | Axis? | Axis.horizontal | 滚动方向(步进可选) | +| context | dynamic | - | 文本内容 | +| style | TDNoticeBarStyle? | - | 公告栏样式 | +| left | Widget? | - | 左侧内容(自定义左侧内容,优先级高于prefixIcon) | +| right | Widget? | - | 侧内容(自定义右侧内容,优先级高于suffixIcon) | +| marquee | bool? | false | 跑马灯效果 | +| speed | double? | 50 | 滚动速度 | +| interval | int? | 3000 | 步进滚动间隔时间(毫秒) | +| direction | Axis? | Axis.horizontal | 滚动方向 | +| theme | TDNoticeBarThemez? | TDNoticeBarThemez.info | 主题 | +| prefixIcon | IconData? | - | 左侧图标 | +| suffixIcon | IconData? | - | 右侧图标 | +| onTap | ValueChanged? | - | 点击事件 | + +``` +``` +### TDNoticeBarStyle +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| context | BuildContext? | - | 上下文 | +| backgroundColor | Color? | - | 公告栏的背景色 | +| leftIconColor | Color? | - | 公告栏左侧图标的颜色 | +| rightIconColor | Color? | - | 公告栏右侧图标的颜色 | +| padding | EdgeInsetsGeometry? | EdgeInsets.only(top: 13, bottom: 13, left: 16, right: 12) | 公告栏的内边距 | +| textStyle | TextStyle? | TextStyle(color: TDTheme.of(context).fontGyColor1, fontSize: 16, height: 1) | 公告栏内容的文本样式 | + diff --git a/tdesign-component/example/lib/page/td_notice_bar_page.dart b/tdesign-component/example/lib/page/td_notice_bar_page.dart index 334e12b67..75bb36275 100644 --- a/tdesign-component/example/lib/page/td_notice_bar_page.dart +++ b/tdesign-component/example/lib/page/td_notice_bar_page.dart @@ -2,7 +2,9 @@ /// @Author lwb /// @Date 2024/5/28 +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; import 'package:tdesign_flutter/tdesign_flutter.dart'; import '../annotation/demo.dart'; @@ -21,16 +23,28 @@ class TDNoticeBarPage extends StatelessWidget { ExampleModule(title: '组件类型', children: [ ExampleItem(desc: '纯文字的公告栏', builder: _textNoticeBar), ExampleItem(desc: '可滚动的公告栏', builder: _scrollNoticeBar), - // ExampleItem(builder: _scrollIconNoticeBar), - // ExampleItem(desc: '步进滚动通知', builder: _stepNoticeBar), - // ExampleItem(desc: '带图标步进通知', builder: _stepIconNoticeBar), - // ExampleItem(desc: '带图标垂直步进通知', builder: _stepVerticalIconNoticeBar), + ExampleItem(builder: _scrollIconNoticeBar), + ExampleItem(desc: '带图标的公告栏', builder: _iconNoticeBar), + ExampleItem(desc: '带关闭的公告栏', builder: _closeNoticeBar), + ExampleItem(desc: '带入口的公告栏', builder: _entranceNoticeBar1), + ExampleItem(builder: _entranceNoticeBar2), + ExampleItem(desc: '自定义样式的公告栏', builder: _customNoticeBar), + ]), + ExampleModule(title: '组件状态', children: [ + ExampleItem(desc: '普通通知', builder: _normalNoticeBar), + ExampleItem(desc: '成功通知', builder: _successNoticeBar), + ExampleItem(desc: '警示通知', builder: _warningNoticeBar), + ExampleItem(desc: '错误通知', builder: _errorNoticeBar), ]), ExampleModule(title: '组件样式', children: [ - // ExampleItem(desc: '背景色', builder: _setBgColorNoticeBar), - // ExampleItem(desc: '文字大小', builder: _setFontSizeNoticeBar), + ExampleItem(desc: '卡片顶部', builder: _cardNoticeBar), ]) ], + test: [ + ExampleItem(desc: '带点击事件的公告栏', builder: _tapNoticeBar), + ExampleItem(desc: '自定义左侧内容的公告栏', builder: _leftNoticeBar), + ExampleItem(desc: '垂直滚动的公告栏', builder: _stepNoticeBar), + ], ); } } @@ -50,63 +64,193 @@ Widget _scrollNoticeBar(BuildContext context) { } @Demo(group: 'noticeBar') -Widget _stepNoticeBar(BuildContext context) { +Widget _scrollIconNoticeBar(BuildContext context) { + return const Padding( + padding: EdgeInsets.only(top: 16), + child: TDNoticeBar( + context: '提示文字描述提示文字描述提示文字描述提示文字描述提示文字', + speed: 50, + prefixIcon: TDIcons.sound, + marquee: true, + ), + ); +} + +@Demo(group: 'noticeBar') +Widget _iconNoticeBar(BuildContext context) { return const TDNoticeBar( - context: ['这是第一条通知', '这是第二条通知', '这是第三条通知'], - interval: 2000, - speed: 50, + context: '这是一条普通的通知信息', + prefixIcon: TDIcons.error_circle_filled, ); } @Demo(group: 'noticeBar') -Widget _scrollIconNoticeBar(BuildContext context) { - return Padding( - padding: const EdgeInsets.only(top: 16), +Widget _closeNoticeBar(BuildContext context) { + return const TDNoticeBar( + context: '这是一条普通的通知信息', + prefixIcon: TDIcons.error_circle_filled, + suffixIcon: TDIcons.close, + ); +} + +@Demo(group: 'noticeBar') +Widget _entranceNoticeBar1(BuildContext context) { + return const TDNoticeBar( + context: '这是一条普通的通知信息', + prefixIcon: TDIcons.error_circle_filled, + right: TDButton( + text: '文字按钮', + type: TDButtonType.text, + theme: TDButtonTheme.primary, + ), + ); +} + +@Demo(group: 'noticeBar') +Widget _entranceNoticeBar2(BuildContext context) { + return const Padding( + padding: EdgeInsets.only(top: 16), child: TDNoticeBar( - context: '这是一条滚动通知', - speed: 50, - left: Icon(TDIcons.sound, color: TDTheme.of(context).brandNormalColor), + context: '这是一条普通的通知信息', + prefixIcon: TDIcons.error_circle_filled, + suffixIcon: TDIcons.chevron_right, ), ); } @Demo(group: 'noticeBar') -Widget _stepIconNoticeBar(BuildContext context) { +Widget _customNoticeBar(BuildContext context) { return TDNoticeBar( - context: const ['这是第一条通知', '这是第二条通知', '这是第三条通知'], - interval: 2000, - speed: 50, - left: Icon(TDIcons.sound, color: TDTheme.of(context).brandNormalColor), - right: Icon(TDIcons.chevron_right, color: TDTheme.of(context).grayColor8), + context: '这是一条普通的通知信息', + prefixIcon: TDIcons.notification, + suffixIcon: TDIcons.chevron_right, + style: TDNoticeBarStyle(backgroundColor: TDTheme.of(context).grayColor3), ); } @Demo(group: 'noticeBar') -Widget _stepVerticalIconNoticeBar(BuildContext context) { - return TDNoticeBar( - context: const ['这是第一条通知', '这是第二条通知', '这是第三条通知'], - interval: 2000, - speed: 300, - direction: Axis.vertical, - left: Icon(TDIcons.sound, color: TDTheme.of(context).brandNormalColor), - right: Icon(TDIcons.chevron_right, color: TDTheme.of(context).grayColor8), +Widget _normalNoticeBar(BuildContext context) { + return const TDNoticeBar( + context: '这是一条普通的通知信息', + prefixIcon: TDIcons.error_circle_filled, + theme: TDNoticeBarTheme.info, ); } @Demo(group: 'noticeBar') -Widget _setBgColorNoticeBar(BuildContext context) { - return TDNoticeBar( - context: '这是一条滚动通知', - style: TDNoticeBarStyle(textStyle: const TextStyle(color: Colors.white)), - speed: 50, +Widget _successNoticeBar(BuildContext context) { + return const TDNoticeBar( + context: '这是一条普通的通知信息', + prefixIcon: TDIcons.error_circle_filled, + theme: TDNoticeBarTheme.success, + ); +} + +@Demo(group: 'noticeBar') +Widget _warningNoticeBar(BuildContext context) { + return const TDNoticeBar( + context: '这是一条普通的通知信息', + prefixIcon: TDIcons.error_circle_filled, + theme: TDNoticeBarTheme.warning, + ); +} + +@Demo(group: 'noticeBar') +Widget _errorNoticeBar(BuildContext context) { + return const TDNoticeBar( + context: '这是一条普通的通知信息', + prefixIcon: TDIcons.error_circle_filled, + theme: TDNoticeBarTheme.error, ); } @Demo(group: 'noticeBar') -Widget _setFontSizeNoticeBar(BuildContext context) { +Widget _cardNoticeBar(BuildContext context) { + var size = MediaQuery.of(context).size; + return Container( + margin: const EdgeInsets.symmetric(horizontal: 16), + decoration: BoxDecoration( + color: TDNoticeBarStyle.generateTheme(context).backgroundColor, + borderRadius: const BorderRadius.all(Radius.circular(9)), + boxShadow: const [ + BoxShadow( + color: Color(0x0d000000), + blurRadius: 8, + spreadRadius: 2, + offset: Offset(0, 2), + ), + BoxShadow( + color: Color(0x0f000000), + blurRadius: 10, + spreadRadius: 1, + offset: Offset(0, 8), + ), + BoxShadow( + color: Color(0x1a000000), + blurRadius: 5, + spreadRadius: -3, + offset: Offset(0, 5), + ), + ], + ), + child: Column( + children: [ + Container( + width: size.width - 32, + decoration: const BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(12)), + ), + clipBehavior: Clip.hardEdge, + child: const TDNoticeBar( + context: '这是一条普通的通知信息', + prefixIcon: TDIcons.error_circle_filled, + suffixIcon: TDIcons.chevron_right, + ), + ), + Container( + height: 150, + decoration: const BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.all(Radius.circular(12)), + ), + ) + ], + ), + ); +} + +@Demo(group: 'noticeBar') +Widget _tapNoticeBar(BuildContext context) { return TDNoticeBar( - context: '这是一条滚动通知', - style: TDNoticeBarStyle(textStyle: const TextStyle(fontSize: 24)), - speed: 50, + context: '这是一条普通的通知信息', + prefixIcon: TDIcons.error_circle_filled, + suffixIcon: TDIcons.chevron_right, + onTap: (trigger) { + TDToast.showText('tap:$trigger', context: context); + }, + ); +} + +@Demo(group: 'noticeBar') +Widget _leftNoticeBar(BuildContext context) { + return const TDNoticeBar( + context: '这是一条普通的通知信息', + suffixIcon: TDIcons.chevron_right, + left: TDButton( + text: '文本', + type: TDButtonType.text, + theme: TDButtonTheme.primary, + size: TDButtonSize.small, + ), + ); +} + +@Demo(group: 'noticeBar') +Widget _stepNoticeBar(BuildContext context) { + return const TDNoticeBar( + context: ['君不见黄河之水天上来', '奔流到海不复回', '君不见'], + direction: Axis.vertical, + prefixIcon: TDIcons.sound, + marquee: true, ); } diff --git a/tdesign-component/lib/src/components/notice_bar/td_notice_bar.dart b/tdesign-component/lib/src/components/notice_bar/td_notice_bar.dart index 8d24e0341..71ef65f03 100644 --- a/tdesign-component/lib/src/components/notice_bar/td_notice_bar.dart +++ b/tdesign-component/lib/src/components/notice_bar/td_notice_bar.dart @@ -1,12 +1,13 @@ import 'dart:async'; import 'dart:convert'; +import 'package:flutter/cupertino.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; import '../../../tdesign_flutter.dart'; -/// 用于警告或提示 class TDNoticeBar extends StatefulWidget { const TDNoticeBar({ super.key, @@ -21,18 +22,19 @@ class TDNoticeBar extends StatefulWidget { this.theme = TDNoticeBarTheme.info, this.prefixIcon, this.suffixIcon, + this.onTap, }); /// 文本内容 final dynamic context; - /// 文字样式 + /// 公告栏样式 final TDNoticeBarStyle? style; - /// 左侧内容 + /// 左侧内容(自定义左侧内容,优先级高于prefixIcon) final Widget? left; - /// 右侧内容 + /// 右侧内容(自定义右侧内容,优先级高于suffixIcon) final Widget? right; /// 跑马灯效果 @@ -44,18 +46,21 @@ class TDNoticeBar extends StatefulWidget { /// 步进滚动间隔时间(毫秒) final int? interval; - /// 滚动方向(步进可选) + /// 滚动方向 final Axis? direction; - /// 内置主题 + /// 主题 final TDNoticeBarTheme? theme; - /// 前置图标 + /// 左侧图标 final IconData? prefixIcon; /// 右侧图标 final IconData? suffixIcon; + /// 点击事件 + final ValueChanged? onTap; + @override State createState() => _TDNoticeBarState(); } @@ -68,6 +73,8 @@ class _TDNoticeBarState extends State { Color? _backgroundColor; Widget? _left; Widget? _right; + final GlobalKey _key = GlobalKey(); + final GlobalKey _contextKey = GlobalKey(); @override void initState() { @@ -76,7 +83,7 @@ class _TDNoticeBarState extends State { throw Exception('speed must not be less than 0'); } if (widget.interval! <= 0) { - throw Exception('stepDuration must not be less than 0'); + throw Exception('interval must not be less than 0'); } _scrollController = ScrollController(); _init(); @@ -114,37 +121,83 @@ class _TDNoticeBarState extends State { } void _scroll() { - var scrollDistance = _getContextWidth() + _size!.width; - if (_getContextWidth() > _size!.width) { - scrollDistance = _getContextWidth() + _getEmptyWidth(); - } + var scrollDistance = + _getContextWidth() + (_size!.width - _style!.getPadding.horizontal); + var remainder = scrollDistance % widget.speed!; _scrollController!.jumpTo(0); var offset = 0.0 + widget.speed!; _scrollController!.animateTo(offset, duration: const Duration(seconds: 1), curve: Curves.linear); _timer = Timer.periodic(const Duration(seconds: 1), (timer) async { - offset += widget.speed!; - if (offset >= scrollDistance) { - // 最后一阶段未滚动距离转化时间比(毫秒数) - offset = 50; + if (offset < scrollDistance - remainder) { + offset += widget.speed!; + await _scrollController!.animateTo(offset, + duration: const Duration(seconds: 1), curve: Curves.linear); + } else { + // 剩余距离小于50 先滚动这部分 然后滚动剩余部分 + // 剩余距离滚动所需时间 + var time = (remainder / widget.speed! * 1000).round(); + // 滚动最后一部分(触底) + await _scrollController!.animateTo(scrollDistance, + duration: Duration(milliseconds: time), curve: Curves.linear); + // 回到顶部(衔接) _scrollController!.jumpTo(0); + // 修改起始位置 + offset = widget.speed! - remainder; + // 计算新起点最后阶段滚动距离 + remainder = (scrollDistance - offset) % widget.speed!; + // 滚动至新起点(弥补触底speed滚动长度) + await _scrollController!.animateTo(offset, + duration: Duration(milliseconds: 1000 - time), + curve: Curves.linear); } - _scrollController!.animateTo(offset, - duration: const Duration(seconds: 1), curve: Curves.linear); }); } - void _step() {} + void _step() { + var textHeight = _getFontSize().height; + var step = 0; + var offset = 0.0; + _timer = + Timer.periodic(Duration(milliseconds: widget.interval!), (timer) { + var time = (textHeight / widget.speed! * 1000).round(); + if (step >= widget.context.length) { + step = 0; + offset = 0; + _scrollController!.jumpTo(0); + } + step++; + offset += textHeight; + _scrollController!.animateTo(offset, + duration: Duration(milliseconds: time), curve: Curves.linear); + }); + } - double _getFontSize() { - return _style!.getTextStyle.fontSize ?? 16; + /// 获取文本内容尺寸消息 + Size _getFontSize() { + var text = widget.context; + if (widget.context is List) { + text = widget.context[0]; + } + final textPainter = TextPainter( + text: TextSpan( + text: text, + style: _style!.getTextStyle, + ), + locale: Localizations.localeOf(context), + textDirection: TextDirection.ltr, + maxLines: 1, + )..layout(maxWidth: _size!.width); + return textPainter.size; } + /// 设置左侧内容 void _setLeftWidget() { if (widget.prefixIcon != null) { _left = Icon( widget.prefixIcon, - color: widget.style?.leftIconColor, + color: _style!.leftIconColor, + size: 24, ); } if (widget.left != null) { @@ -152,11 +205,13 @@ class _TDNoticeBarState extends State { } } + /// 设置右侧内容 void _setRightWidget() { if (widget.suffixIcon != null) { _right = Icon( widget.suffixIcon, - color: widget.style?.rightIconColor, + color: _style!.rightIconColor, + size: 24, ); } if (widget.right != null) { @@ -164,39 +219,64 @@ class _TDNoticeBarState extends State { } } + /// 获取文本内容宽度 double _getContextWidth() { - var contextWidth = _getFontSize() * widget.context!.length; - if (contextWidth > _size!.width) { - return contextWidth; + var contextWidth = + _key.currentContext?.findRenderObject()?.paintBounds.size.width ?? 0; + if (contextWidth == 0) { + contextWidth = _getFontSize().width; } - return _size!.width; + return contextWidth; } + /// 获取滚动区域宽度 double _getEmptyWidth() { - print('32ssss::::::::::${_style!.getPadding}:::${_style!.getPadding.horizontal}'); - return _size!.width - _style!.getPadding.horizontal; + return _contextKey.currentContext + ?.findRenderObject() + ?.paintBounds + .size + .width ?? + (_size!.width - _style!.getPadding.horizontal); } + /// 内容区域 Widget _contextWidget() { - Widget textWidget = TDText(widget.context, style: _style?.getTextStyle); - Widget? child = textWidget; + var valid = false; + Widget? textWidget; + if (widget.context is String) { + valid = true; + textWidget = TDText(widget.context, style: _style?.getTextStyle); + } + if (widget.context is List) { + valid = true; + textWidget = TDText(widget.context[0], style: _style?.getTextStyle); + } + if (!valid) { + throw Exception('context must be String or List'); + } + if (widget.marquee == false) { + return textWidget!; + } + Widget? child; switch (widget.direction) { case Axis.horizontal: child = SingleChildScrollView( controller: _scrollController, - scrollDirection: widget.direction!, - // physics: const NeverScrollableScrollPhysics(), + scrollDirection: Axis.horizontal, + physics: const NeverScrollableScrollPhysics(), child: Row( children: [ SizedBox( - width: _getContextWidth(), - height: _getFontSize(), + key: _key, + height: _getFontSize().height, child: textWidget, ), SizedBox(width: _getEmptyWidth()), SizedBox( - width: _getContextWidth(), - height: _getFontSize(), + width: _getEmptyWidth() > _getContextWidth() + ? _getEmptyWidth() + : _getContextWidth(), + height: _getFontSize().height, child: textWidget, ) ], @@ -204,39 +284,87 @@ class _TDNoticeBarState extends State { ); break; case Axis.vertical: + var contexts = widget.context as List; + child = SizedBox( + height: _getFontSize().height, + child: SingleChildScrollView( + controller: _scrollController, + scrollDirection: Axis.vertical, + // physics: const NeverScrollableScrollPhysics(), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + for (int i = 0; i < contexts.length; i++) + SizedBox( + child: TDText( + contexts[i], + style: _style!.getTextStyle, + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + SizedBox( + key: _key, + child: TDText( + contexts[0], + style: _style?.getTextStyle, + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ) + ]), + ), + ); break; default: child = textWidget; break; } - return child; + return child!; + } + + void _onTap(trigger) { + if (widget.onTap != null) { + widget.onTap!(trigger); + } } @override Widget build(BuildContext context) { _size = MediaQuery.of(context).size; return Container( - width: _size!.width, - height: _getFontSize() + _style!.getPadding.vertical, padding: _style!.getPadding, decoration: BoxDecoration( color: _backgroundColor, ), child: Row( + crossAxisAlignment: CrossAxisAlignment.center, children: [ Visibility( visible: _left != null, - child: Container( - child: _left, + child: GestureDetector( + onTap: () => _onTap('prefix-icon'), + child: Container( + margin: const EdgeInsets.only(right: 8), + child: _left, + ), ), ), Expanded( - child: _contextWidget(), + key: _contextKey, + child: GestureDetector( + onTap: () => _onTap('context'), + child: _contextWidget(), + ), ), Visibility( visible: _right != null, - child: Container( - child: _right, + child: GestureDetector( + onTap: () => _onTap('suffix-icon'), + child: Container( + child: _right, + ), ), ), ], diff --git a/tdesign-component/lib/src/components/notice_bar/td_notice_bar_style.dart b/tdesign-component/lib/src/components/notice_bar/td_notice_bar_style.dart index 86a09a623..12a32dbf5 100644 --- a/tdesign-component/lib/src/components/notice_bar/td_notice_bar_style.dart +++ b/tdesign-component/lib/src/components/notice_bar/td_notice_bar_style.dart @@ -16,6 +16,7 @@ enum TDNoticeBarType { step } +/// 公告栏主题 enum TDNoticeBarTheme { /// 默认 info, @@ -69,8 +70,10 @@ class TDNoticeBarStyle { TextStyle( color: TDTheme.of(context).fontGyColor1, fontSize: 16, height: 1); + /// 根据主题生成样式 TDNoticeBarStyle.generateTheme(BuildContext context, {TDNoticeBarTheme? theme = TDNoticeBarTheme.info}) { + rightIconColor = TDTheme.of(context).grayColor7; switch (theme) { case TDNoticeBarTheme.warning: leftIconColor = TDTheme.of(context).warningNormalColor; From 05ee575004f06458369dd64b7370257c7172d1c4 Mon Sep 17 00:00:00 2001 From: ccXxx1aoBai <1426169428@qq.com> Date: Tue, 16 Jul 2024 15:33:13 +0800 Subject: [PATCH 09/11] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E7=A4=BA=E4=BE=8B?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2=E8=83=8C=E6=99=AF=E8=89=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tdesign-component/example/lib/page/td_notice_bar_page.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/tdesign-component/example/lib/page/td_notice_bar_page.dart b/tdesign-component/example/lib/page/td_notice_bar_page.dart index 75bb36275..0693ead0e 100644 --- a/tdesign-component/example/lib/page/td_notice_bar_page.dart +++ b/tdesign-component/example/lib/page/td_notice_bar_page.dart @@ -19,6 +19,7 @@ class TDNoticeBarPage extends StatelessWidget { title: tdTitle(context), exampleCodeGroup: 'noticeBar', desc: '在导航栏下方,用于给用户显示提示消息。', + backgroundColor: Colors.white, children: [ ExampleModule(title: '组件类型', children: [ ExampleItem(desc: '纯文字的公告栏', builder: _textNoticeBar), From db105353cf066ab0868cc0b8723f3c20d0e0813d Mon Sep 17 00:00:00 2001 From: ccXxx1aoBai <1426169428@qq.com> Date: Thu, 18 Jul 2024 16:47:25 +0800 Subject: [PATCH 10/11] =?UTF-8?q?=E7=BB=84=E4=BB=B6=E6=A0=B7=E5=BC=8F?= =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../example/lib/page/td_notice_bar_page.dart | 7 +- .../components/notice_bar/td_notice_bar.dart | 69 ++++++++++++------- .../notice_bar/td_notice_bar_style.dart | 7 +- 3 files changed, 55 insertions(+), 28 deletions(-) diff --git a/tdesign-component/example/lib/page/td_notice_bar_page.dart b/tdesign-component/example/lib/page/td_notice_bar_page.dart index 0693ead0e..770f9301a 100644 --- a/tdesign-component/example/lib/page/td_notice_bar_page.dart +++ b/tdesign-component/example/lib/page/td_notice_bar_page.dart @@ -103,6 +103,9 @@ Widget _entranceNoticeBar1(BuildContext context) { text: '文字按钮', type: TDButtonType.text, theme: TDButtonTheme.primary, + size: TDButtonSize.extraSmall, + height: 22, + padding: EdgeInsets.symmetric(vertical: 0, horizontal: 0), ), ); } @@ -241,7 +244,9 @@ Widget _leftNoticeBar(BuildContext context) { text: '文本', type: TDButtonType.text, theme: TDButtonTheme.primary, - size: TDButtonSize.small, + size: TDButtonSize.extraSmall, + height: 22, + padding: EdgeInsets.symmetric(vertical: 0, horizontal: 0), ), ); } diff --git a/tdesign-component/lib/src/components/notice_bar/td_notice_bar.dart b/tdesign-component/lib/src/components/notice_bar/td_notice_bar.dart index 71ef65f03..ba7d030c1 100644 --- a/tdesign-component/lib/src/components/notice_bar/td_notice_bar.dart +++ b/tdesign-component/lib/src/components/notice_bar/td_notice_bar.dart @@ -155,19 +155,18 @@ class _TDNoticeBarState extends State { } void _step() { - var textHeight = _getFontSize().height; var step = 0; var offset = 0.0; - _timer = - Timer.periodic(Duration(milliseconds: widget.interval!), (timer) { - var time = (textHeight / widget.speed! * 1000).round(); + _timer = Timer.periodic(Duration(milliseconds: widget.interval!), (timer) { + var time = (22 / widget.speed! * 1000).round(); if (step >= widget.context.length) { step = 0; offset = 0; _scrollController!.jumpTo(0); } step++; - offset += textHeight; + // 固定滚动行高(22) + offset += 22; _scrollController!.animateTo(offset, duration: Duration(milliseconds: time), curve: Curves.linear); }); @@ -197,7 +196,7 @@ class _TDNoticeBarState extends State { _left = Icon( widget.prefixIcon, color: _style!.leftIconColor, - size: 24, + size: 22, ); } if (widget.left != null) { @@ -211,7 +210,7 @@ class _TDNoticeBarState extends State { _right = Icon( widget.suffixIcon, color: _style!.rightIconColor, - size: 24, + size: 22, ); } if (widget.right != null) { @@ -245,11 +244,23 @@ class _TDNoticeBarState extends State { Widget? textWidget; if (widget.context is String) { valid = true; - textWidget = TDText(widget.context, style: _style?.getTextStyle); + textWidget = SizedBox( + height: 22, + child: Align( + alignment: Alignment.centerLeft, + child: TDText(widget.context, style: _style?.getTextStyle, maxLines: 1), + ), + ); } if (widget.context is List) { valid = true; - textWidget = TDText(widget.context[0], style: _style?.getTextStyle); + textWidget = SizedBox( + height: 22, + child: Align( + alignment: Alignment.centerLeft, + child: TDText(widget.context[0], style: _style?.getTextStyle, maxLines: 1), + ), + ); } if (!valid) { throw Exception('context must be String or List'); @@ -268,7 +279,7 @@ class _TDNoticeBarState extends State { children: [ SizedBox( key: _key, - height: _getFontSize().height, + height: 22, child: textWidget, ), SizedBox(width: _getEmptyWidth()), @@ -276,7 +287,7 @@ class _TDNoticeBarState extends State { width: _getEmptyWidth() > _getContextWidth() ? _getEmptyWidth() : _getContextWidth(), - height: _getFontSize().height, + height: 22, child: textWidget, ) ], @@ -286,7 +297,7 @@ class _TDNoticeBarState extends State { case Axis.vertical: var contexts = widget.context as List; child = SizedBox( - height: _getFontSize().height, + height: 22, child: SingleChildScrollView( controller: _scrollController, scrollDirection: Axis.vertical, @@ -297,22 +308,30 @@ class _TDNoticeBarState extends State { children: [ for (int i = 0; i < contexts.length; i++) SizedBox( - child: TDText( - contexts[i], - style: _style!.getTextStyle, - maxLines: 1, - overflow: TextOverflow.ellipsis, + height: 22, + child: Align( + alignment: Alignment.centerLeft, + child: TDText( + contexts[i], + style: _style!.getTextStyle, + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), ), ), SizedBox( key: _key, - child: TDText( - contexts[0], - style: _style?.getTextStyle, - maxLines: 1, - overflow: TextOverflow.ellipsis, + height: 22, + child: Align( + alignment: Alignment.centerLeft, + child: TDText( + contexts[0], + style: _style?.getTextStyle, + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), ), - ) + ), ]), ), ); @@ -362,9 +381,7 @@ class _TDNoticeBarState extends State { visible: _right != null, child: GestureDetector( onTap: () => _onTap('suffix-icon'), - child: Container( - child: _right, - ), + child: _right, ), ), ], diff --git a/tdesign-component/lib/src/components/notice_bar/td_notice_bar_style.dart b/tdesign-component/lib/src/components/notice_bar/td_notice_bar_style.dart index 12a32dbf5..b08c4e184 100644 --- a/tdesign-component/lib/src/components/notice_bar/td_notice_bar_style.dart +++ b/tdesign-component/lib/src/components/notice_bar/td_notice_bar_style.dart @@ -68,7 +68,12 @@ class TDNoticeBarStyle { TextStyle get getTextStyle => textStyle ?? TextStyle( - color: TDTheme.of(context).fontGyColor1, fontSize: 16, height: 1); + color: TDTheme.of(context).fontGyColor1, + fontSize: 14, + height: 1, + fontWeight: FontWeight.normal, + fontStyle: FontStyle.normal, + ); /// 根据主题生成样式 TDNoticeBarStyle.generateTheme(BuildContext context, From c67b6f6f50b7afa50a0366fba15c016a787fb622 Mon Sep 17 00:00:00 2001 From: ccXxx1aoBai <1426169428@qq.com> Date: Thu, 18 Jul 2024 18:26:27 +0800 Subject: [PATCH 11/11] =?UTF-8?q?=E6=96=87=E5=AD=97=E5=B1=85=E4=B8=AD?= =?UTF-8?q?=E3=80=81=E5=A2=9E=E5=8A=A0height=E5=B1=9E=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/notice_bar/td_notice_bar.dart | 41 +++++++++++++------ 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/tdesign-component/lib/src/components/notice_bar/td_notice_bar.dart b/tdesign-component/lib/src/components/notice_bar/td_notice_bar.dart index ba7d030c1..591caa303 100644 --- a/tdesign-component/lib/src/components/notice_bar/td_notice_bar.dart +++ b/tdesign-component/lib/src/components/notice_bar/td_notice_bar.dart @@ -23,6 +23,7 @@ class TDNoticeBar extends StatefulWidget { this.prefixIcon, this.suffixIcon, this.onTap, + this.height = 22, }); /// 文本内容 @@ -61,6 +62,9 @@ class TDNoticeBar extends StatefulWidget { /// 点击事件 final ValueChanged? onTap; + /// 文字高度 (当使用prefixIcon或suffixIcon时,icon大小值等于该属性) + final double height; + @override State createState() => _TDNoticeBarState(); } @@ -158,7 +162,7 @@ class _TDNoticeBarState extends State { var step = 0; var offset = 0.0; _timer = Timer.periodic(Duration(milliseconds: widget.interval!), (timer) { - var time = (22 / widget.speed! * 1000).round(); + var time = (widget.height / widget.speed! * 1000).round(); if (step >= widget.context.length) { step = 0; offset = 0; @@ -166,7 +170,7 @@ class _TDNoticeBarState extends State { } step++; // 固定滚动行高(22) - offset += 22; + offset += widget.height; _scrollController!.animateTo(offset, duration: Duration(milliseconds: time), curve: Curves.linear); }); @@ -196,7 +200,7 @@ class _TDNoticeBarState extends State { _left = Icon( widget.prefixIcon, color: _style!.leftIconColor, - size: 22, + size: widget.height, ); } if (widget.left != null) { @@ -210,7 +214,7 @@ class _TDNoticeBarState extends State { _right = Icon( widget.suffixIcon, color: _style!.rightIconColor, - size: 22, + size: widget.height, ); } if (widget.right != null) { @@ -245,20 +249,30 @@ class _TDNoticeBarState extends State { if (widget.context is String) { valid = true; textWidget = SizedBox( - height: 22, + height: widget.height, child: Align( alignment: Alignment.centerLeft, - child: TDText(widget.context, style: _style?.getTextStyle, maxLines: 1), + child: TDText( + widget.context, + style: _style?.getTextStyle, + maxLines: 1, + forceVerticalCenter: true, + ), ), ); } if (widget.context is List) { valid = true; textWidget = SizedBox( - height: 22, + height: widget.height, child: Align( alignment: Alignment.centerLeft, - child: TDText(widget.context[0], style: _style?.getTextStyle, maxLines: 1), + child: TDText( + widget.context[0], + style: _style?.getTextStyle, + maxLines: 1, + forceVerticalCenter: true, + ), ), ); } @@ -279,7 +293,7 @@ class _TDNoticeBarState extends State { children: [ SizedBox( key: _key, - height: 22, + height: widget.height, child: textWidget, ), SizedBox(width: _getEmptyWidth()), @@ -287,7 +301,7 @@ class _TDNoticeBarState extends State { width: _getEmptyWidth() > _getContextWidth() ? _getEmptyWidth() : _getContextWidth(), - height: 22, + height: widget.height, child: textWidget, ) ], @@ -297,7 +311,7 @@ class _TDNoticeBarState extends State { case Axis.vertical: var contexts = widget.context as List; child = SizedBox( - height: 22, + height: widget.height, child: SingleChildScrollView( controller: _scrollController, scrollDirection: Axis.vertical, @@ -308,7 +322,7 @@ class _TDNoticeBarState extends State { children: [ for (int i = 0; i < contexts.length; i++) SizedBox( - height: 22, + height: widget.height, child: Align( alignment: Alignment.centerLeft, child: TDText( @@ -321,7 +335,7 @@ class _TDNoticeBarState extends State { ), SizedBox( key: _key, - height: 22, + height: widget.height, child: Align( alignment: Alignment.centerLeft, child: TDText( @@ -359,6 +373,7 @@ class _TDNoticeBarState extends State { ), child: Row( crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.start, children: [ Visibility( visible: _left != null,