diff --git a/lib/common/widgets/custom_icon.dart b/lib/common/widgets/custom_icon.dart index aaa163c63..aa610f1b9 100644 --- a/lib/common/widgets/custom_icon.dart +++ b/lib/common/widgets/custom_icon.dart @@ -3,27 +3,25 @@ import 'package:flutter/widgets.dart'; class CustomIcon { - static const _kFontFam = 'custom_icon'; - - static const IconData coin = IconData(0xe800, fontFamily: _kFontFam); - static const IconData dm_off = IconData(0xe801, fontFamily: _kFontFam); - static const IconData dm_on = IconData(0xe802, fontFamily: _kFontFam); - static const IconData dm_settings = IconData(0xe803, fontFamily: _kFontFam); - static const IconData dyn = IconData(0xe804, fontFamily: _kFontFam); - static const IconData fav = IconData(0xe805, fontFamily: _kFontFam); - static const IconData share = IconData(0xe806, fontFamily: _kFontFam); - static const IconData share_line = IconData(0xe807, fontFamily: _kFontFam); - static const IconData share_node = IconData(0xe808, fontFamily: _kFontFam); - static const IconData star_favorite_line = - IconData(0xe809, fontFamily: _kFontFam); - static const IconData thumbs_down = IconData(0xe80a, fontFamily: _kFontFam); - static const IconData thumbs_down_outline = - IconData(0xe80b, fontFamily: _kFontFam); - static const IconData thumbs_up = IconData(0xe80c, fontFamily: _kFontFam); - static const IconData thumbs_up_line = - IconData(0xe80d, fontFamily: _kFontFam); - static const IconData thumbs_up_outline = - IconData(0xe80e, fontFamily: _kFontFam); - static const IconData topic_tag = IconData(0xe80f, fontFamily: _kFontFam); - static const IconData watch_later = IconData(0xe810, fontFamily: _kFontFam); + static const IconData coin = _CustomIconData(0xe800); + static const IconData dm_off = _CustomIconData(0xe801); + static const IconData dm_on = _CustomIconData(0xe802); + static const IconData dm_settings = _CustomIconData(0xe803); + static const IconData dyn = _CustomIconData(0xe804); + static const IconData fav = _CustomIconData(0xe805); + static const IconData share = _CustomIconData(0xe806); + static const IconData share_line = _CustomIconData(0xe807); + static const IconData share_node = _CustomIconData(0xe808); + static const IconData star_favorite_line = _CustomIconData(0xe809); + static const IconData thumbs_down = _CustomIconData(0xe80a); + static const IconData thumbs_down_outline = _CustomIconData(0xe80b); + static const IconData thumbs_up = _CustomIconData(0xe80c); + static const IconData thumbs_up_line = _CustomIconData(0xe80d); + static const IconData thumbs_up_outline = _CustomIconData(0xe80e); + static const IconData topic_tag = _CustomIconData(0xe80f); + static const IconData watch_later = _CustomIconData(0xe810); +} + +class _CustomIconData extends IconData { + const _CustomIconData(super.codePoint) : super(fontFamily: 'custom_icon'); } diff --git a/lib/common/widgets/video_popup_menu.dart b/lib/common/widgets/video_popup_menu.dart index 57a720bdd..d5451172c 100644 --- a/lib/common/widgets/video_popup_menu.dart +++ b/lib/common/widgets/video_popup_menu.dart @@ -33,7 +33,7 @@ class VideoCustomActions { VideoCustomAction( videoItem.bvid!, 'copy', - Stack( + const Stack( clipBehavior: Clip.none, children: [ Icon(MdiIcons.identifier, size: 16), @@ -47,7 +47,7 @@ class VideoCustomActions { VideoCustomAction( '稍后再看', 'pause', - Icon(MdiIcons.clockTimeEightOutline, size: 16), + const Icon(MdiIcons.clockTimeEightOutline, size: 16), () async { var res = await UserHttp.toViewLater(bvid: videoItem.bvid); SmartDialog.showToast(res['msg']); @@ -58,7 +58,7 @@ class VideoCustomActions { VideoCustomAction( '访问:${videoItem.owner.name}', 'visit', - Icon(MdiIcons.accountCircleOutline, size: 16), + const Icon(MdiIcons.accountCircleOutline, size: 16), () { Get.toNamed('/member?mid=${videoItem.owner.mid}', arguments: { 'heroTag': '${videoItem.owner.mid}', @@ -69,7 +69,7 @@ class VideoCustomActions { VideoCustomAction( '不感兴趣', 'dislike', - Icon(MdiIcons.thumbDownOutline, size: 16), + const Icon(MdiIcons.thumbDownOutline, size: 16), () { String? accessKey = Accounts.get(AccountType.recommend).accessKey; if (accessKey == null || accessKey == "") { @@ -233,7 +233,7 @@ class VideoCustomActions { VideoCustomAction( '拉黑:${videoItem.owner.name}', 'block', - Icon(MdiIcons.cancel, size: 16), + const Icon(MdiIcons.cancel, size: 16), () { showDialog( context: context, @@ -274,12 +274,9 @@ class VideoCustomActions { VideoCustomAction( "${MineController.anonymity.value ? '退出' : '进入'}无痕模式", 'anonymity', - Icon( - MineController.anonymity.value - ? MdiIcons.incognitoOff - : MdiIcons.incognito, - size: 16, - ), + MineController.anonymity.value + ? const Icon(MdiIcons.incognitoOff, size: 16) + : const Icon(MdiIcons.incognito, size: 16), () => MineController.onChangeAnonymity(context), ) ]; diff --git a/lib/models/common/home_tab_type.dart b/lib/models/common/home_tab_type.dart index cd1e5ddc8..7ea5777bd 100644 --- a/lib/models/common/home_tab_type.dart +++ b/lib/models/common/home_tab_type.dart @@ -76,7 +76,7 @@ List get homeTabsConfig => [ 'page': const BangumiPage(tabType: HomeTabType.bangumi), }, { - 'icon': Icon( + 'icon': const Icon( MdiIcons.theater, size: 15, ), diff --git a/lib/models/common/theme/theme_type.dart b/lib/models/common/theme/theme_type.dart index 7f4341e23..62e2916cd 100644 --- a/lib/models/common/theme/theme_type.dart +++ b/lib/models/common/theme/theme_type.dart @@ -18,9 +18,9 @@ extension ThemeTypeExt on ThemeType { ThemeType.system => ThemeMode.system, }; - IconData get iconData => switch (this) { - ThemeType.light => MdiIcons.weatherSunny, - ThemeType.dark => MdiIcons.weatherNight, - ThemeType.system => MdiIcons.themeLightDark, + Icon get icon => switch (this) { + ThemeType.light => const Icon(MdiIcons.weatherSunny, size: 24), + ThemeType.dark => const Icon(MdiIcons.weatherNight, size: 24), + ThemeType.system => const Icon(MdiIcons.themeLightDark, size: 24), }; } diff --git a/lib/pages/about/view.dart b/lib/pages/about/view.dart index 221169d56..6505f5369 100644 --- a/lib/pages/about/view.dart +++ b/lib/pages/about/view.dart @@ -158,7 +158,7 @@ Commit Hash: ${BuildConfig.commitHash}''', onTap: () { Utils.channel.invokeMethod('linkVerifySettings'); }, - leading: Icon(MdiIcons.linkBoxOutline), + leading: const Icon(MdiIcons.linkBoxOutline), title: const Text('打开受支持的链接'), trailing: Icon( Icons.arrow_forward, diff --git a/lib/pages/article/widgets/opus_content.dart b/lib/pages/article/widgets/opus_content.dart index e12faa688..8937b6c19 100644 --- a/lib/pages/article/widgets/opus_content.dart +++ b/lib/pages/article/widgets/opus_content.dart @@ -215,7 +215,7 @@ class OpusContent extends StatelessWidget { children: element.list!.items?.indexed.map((entry) { return TextSpan( children: [ - WidgetSpan( + const WidgetSpan( child: Icon(MdiIcons.circleMedium), alignment: PlaceholderAlignment.middle, ), diff --git a/lib/pages/dynamics_topic/view.dart b/lib/pages/dynamics_topic/view.dart index 7d95859b7..9e2b2ba8d 100644 --- a/lib/pages/dynamics_topic/view.dart +++ b/lib/pages/dynamics_topic/view.dart @@ -271,7 +271,7 @@ class _DynTopicPageState extends State { Utils.shareText( '${_controller.topicName} https://m.bilibili.com/topic-detail?topic_id=${_controller.topicId}'); }, - icon: Icon(MdiIcons.share), + icon: const Icon(MdiIcons.share), ), PopupMenuButton( itemBuilder: (context) { diff --git a/lib/pages/history/widgets/item.dart b/lib/pages/history/widgets/item.dart index 96323c07f..c2771c9ef 100644 --- a/lib/pages/history/widgets/item.dart +++ b/lib/pages/history/widgets/item.dart @@ -264,7 +264,7 @@ class HistoryItem extends StatelessWidget { height: 35, child: Row( children: [ - Icon(MdiIcons.accountCircleOutline, size: 16), + const Icon(MdiIcons.accountCircleOutline, size: 16), const SizedBox(width: 6), Text( '访问:${videoItem.authorName}', diff --git a/lib/pages/live_room/widgets/header_control.dart b/lib/pages/live_room/widgets/header_control.dart index 43bb8219b..1fee88f48 100644 --- a/lib/pages/live_room/widgets/header_control.dart +++ b/lib/pages/live_room/widgets/header_control.dart @@ -52,13 +52,17 @@ class LiveHeaderControl extends StatelessWidget implements PreferredSizeWidget { Obx( () => IconButton( onPressed: plPlayerController.setOnlyPlayAudio, - icon: Icon( - size: 18, - plPlayerController.onlyPlayAudio.value - ? MdiIcons.musicCircle - : MdiIcons.musicCircleOutline, - color: Colors.white, - ), + icon: plPlayerController.onlyPlayAudio.value + ? const Icon( + size: 18, + MdiIcons.musicCircle, + color: Colors.white, + ) + : const Icon( + size: 18, + MdiIcons.musicCircleOutline, + color: Colors.white, + ), ), ), const SizedBox(width: 10), diff --git a/lib/pages/mine/controller.dart b/lib/pages/mine/controller.dart index 1bb1402ad..f63d7b3d0 100644 --- a/lib/pages/mine/controller.dart +++ b/lib/pages/mine/controller.dart @@ -109,9 +109,7 @@ class MineController extends GetxController { children: [ Row( children: [ - Icon( - MdiIcons.incognito, - ), + const Icon(MdiIcons.incognito), const SizedBox(width: 10), Text('已进入无痕模式', style: theme.textTheme.titleMedium) ], @@ -183,9 +181,7 @@ class MineController extends GetxController { child: Row( mainAxisAlignment: MainAxisAlignment.start, children: [ - Icon( - MdiIcons.incognitoOff, - ), + const Icon(MdiIcons.incognitoOff), const SizedBox(width: 10), Text('已退出无痕模式', style: theme.textTheme.titleMedium), ], diff --git a/lib/pages/mine/view.dart b/lib/pages/mine/view.dart index e2b95e7f7..aaa4650b3 100644 --- a/lib/pages/mine/view.dart +++ b/lib/pages/mine/view.dart @@ -45,12 +45,9 @@ class _MinePageState extends State { MineController.onChangeAnonymity(context); setState(() {}); }, - icon: Icon( - MineController.anonymity.value - ? MdiIcons.incognito - : MdiIcons.incognitoOff, - size: 24, - ), + icon: MineController.anonymity.value + ? const Icon(MdiIcons.incognito, size: 24) + : const Icon(MdiIcons.incognitoOff, size: 24), ), Obx( () { @@ -62,10 +59,7 @@ class _MinePageState extends State { ), tooltip: '切换至${_mineController.nextThemeType.description}主题', onPressed: _mineController.onChangeTheme, - icon: Icon( - _mineController.themeType.value.iconData, - size: 24, - ), + icon: _mineController.themeType.value.icon, ); }, ), @@ -80,10 +74,7 @@ class _MinePageState extends State { Get.back(), Get.toNamed('/setting', preventDuplicates: false), }, - icon: Icon( - MdiIcons.cogs, - size: 24, - ), + icon: const Icon(MdiIcons.cogs, size: 24), ), const SizedBox(width: 10), ], diff --git a/lib/pages/setting/view.dart b/lib/pages/setting/view.dart index 861d6a215..bc9c10731 100644 --- a/lib/pages/setting/view.dart +++ b/lib/pages/setting/view.dart @@ -80,7 +80,7 @@ class _SettingPageState extends State { subtitle: '震动、搜索、收藏、ai、评论、动态、代理、更新检查等', icon: Icons.extension_outlined, ), - _SettingsModel( + const _SettingsModel( name: 'webdavSetting', title: 'WebDAV 设置', icon: MdiIcons.databaseCogOutline, diff --git a/lib/pages/setting/widgets/model.dart b/lib/pages/setting/widgets/model.dart index 9762bcd0b..2087c880f 100644 --- a/lib/pages/setting/widgets/model.dart +++ b/lib/pages/setting/widgets/model.dart @@ -212,7 +212,7 @@ List get styleSettings => [ SettingsModel( settingsType: SettingsType.sw1tch, title: '优化平板导航栏', - leading: Icon(MdiIcons.soundbar), + leading: const Icon(MdiIcons.soundbar), setKey: SettingBoxKey.optTabletNav, defaultVal: true, needReboot: true, @@ -383,7 +383,7 @@ List get styleSettings => [ } }, title: '消息未读标记', - leading: Icon(MdiIcons.bellBadgeOutline), + leading: const Icon(MdiIcons.bellBadgeOutline), getSubtitle: () => '当前标记样式:${GStorage.msgBadgeMode.description}', ), SettingsModel( @@ -412,7 +412,7 @@ List get styleSettings => [ } }, title: '消息未读类型', - leading: Icon(MdiIcons.bellCogOutline), + leading: const Icon(MdiIcons.bellCogOutline), getSubtitle: () => '当前消息类型:${GStorage.msgUnReadTypeV2.map((item) => item.title).join('、')}', ), @@ -778,14 +778,14 @@ List get playSettings => [ SettingsModel( settingsType: SettingsType.sw1tch, title: '左右侧滑动调节亮度/音量', - leading: Icon(MdiIcons.tuneVerticalVariant), + leading: const Icon(MdiIcons.tuneVerticalVariant), setKey: SettingBoxKey.enableSlideVolumeBrightness, defaultVal: true, ), SettingsModel( settingsType: SettingsType.sw1tch, title: '中间滑动进入/退出全屏', - leading: Icon(MdiIcons.panVertical), + leading: const Icon(MdiIcons.panVertical), setKey: SettingBoxKey.enableSlideFS, defaultVal: true, ), @@ -1029,7 +1029,7 @@ List get videoSettings => [ SettingsModel( settingsType: SettingsType.normal, title: 'CDN 设置', - leading: Icon(MdiIcons.cloudPlusOutline), + leading: const Icon(MdiIcons.cloudPlusOutline), getSubtitle: () => '当前使用:${CDNService.fromCode(GStorage.defaultCDNService).description},部分 CDN 可能失效,如无法播放请尝试切换', onTap: (setState) async { @@ -1057,7 +1057,7 @@ List get videoSettings => [ settingsType: SettingsType.sw1tch, title: '音频不跟随 CDN 设置', subtitle: '直接采用备用 URL,可解决部分视频无声', - leading: Icon(MdiIcons.musicNotePlus), + leading: const Icon(MdiIcons.musicNotePlus), setKey: SettingBoxKey.disableAudioCDN, defaultVal: true, ), @@ -1585,7 +1585,7 @@ List get extraSettings => [ title: '显示视频分段信息', leading: Transform.rotate( angle: pi / 2, - child: Icon(MdiIcons.viewHeadline), + child: const Icon(MdiIcons.viewHeadline), ), setKey: SettingBoxKey.showViewPoints, defaultVal: true, @@ -1593,21 +1593,21 @@ List get extraSettings => [ SettingsModel( settingsType: SettingsType.sw1tch, title: '视频页显示相关视频', - leading: Icon(MdiIcons.motionPlayOutline), + leading: const Icon(MdiIcons.motionPlayOutline), setKey: SettingBoxKey.showRelatedVideo, defaultVal: true, ), SettingsModel( settingsType: SettingsType.sw1tch, title: '显示视频评论', - leading: Icon(MdiIcons.commentTextOutline), + leading: const Icon(MdiIcons.commentTextOutline), setKey: SettingBoxKey.showVideoReply, defaultVal: true, ), SettingsModel( settingsType: SettingsType.sw1tch, title: '显示番剧评论', - leading: Icon(MdiIcons.commentTextOutline), + leading: const Icon(MdiIcons.commentTextOutline), setKey: SettingBoxKey.showBangumiReply, defaultVal: true, ), @@ -1768,7 +1768,7 @@ List get extraSettings => [ settingsType: SettingsType.sw1tch, title: '分P/合集:倒序播放从首集开始播放', subtitle: '开启则自动切换为倒序首集,否则保持当前集', - leading: Icon(MdiIcons.sort), + leading: const Icon(MdiIcons.sort), setKey: SettingBoxKey.reverseFromFirst, defaultVal: true, ), @@ -1878,14 +1878,14 @@ List get extraSettings => [ SettingsModel( settingsType: SettingsType.sw1tch, title: '显示会员彩色弹幕', - leading: Icon(MdiIcons.gradientHorizontal), + leading: const Icon(MdiIcons.gradientHorizontal), setKey: SettingBoxKey.showVipDanmaku, defaultVal: true, ), SettingsModel( settingsType: SettingsType.sw1tch, title: '显示高级弹幕', - leading: Icon(MdiIcons.paletteAdvanced), + leading: const Icon(MdiIcons.paletteAdvanced), setKey: SettingBoxKey.showSpecialDanmaku, defaultVal: false, ), @@ -2049,7 +2049,7 @@ List get extraSettings => [ SettingsModel( settingsType: SettingsType.sw1tch, title: '展示头像/评论/动态装饰', - leading: Icon(MdiIcons.stickerCircleOutline), + leading: const Icon(MdiIcons.stickerCircleOutline), setKey: SettingBoxKey.showDynDecorate, defaultVal: true, onChanged: (value) => PendantAvatar.showDynDecorate = value, @@ -2165,7 +2165,7 @@ List get extraSettings => [ SettingsModel( settingsType: SettingsType.sw1tch, title: '全屏展示点赞/投币/收藏等操作按钮', - leading: Icon(MdiIcons.dotsHorizontalCircleOutline), + leading: const Icon(MdiIcons.dotsHorizontalCircleOutline), setKey: SettingBoxKey.showFSActionItem, defaultVal: true, ), @@ -2186,14 +2186,14 @@ List get extraSettings => [ SettingsModel( settingsType: SettingsType.sw1tch, title: '启用拖拽字幕调整底部边距', - leading: Icon(MdiIcons.dragVariant), + leading: const Icon(MdiIcons.dragVariant), setKey: SettingBoxKey.enableDragSubtitle, defaultVal: false, ), SettingsModel( settingsType: SettingsType.sw1tch, title: '展示追番时间表', - leading: Icon(MdiIcons.chartTimelineVariantShimmer), + leading: const Icon(MdiIcons.chartTimelineVariantShimmer), setKey: SettingBoxKey.showPgcTimeline, defaultVal: true, needReboot: true, diff --git a/lib/pages/video/widgets/header_control.dart b/lib/pages/video/widgets/header_control.dart index 58f65a64b..e5f601861 100644 --- a/lib/pages/video/widgets/header_control.dart +++ b/lib/pages/video/widgets/header_control.dart @@ -251,7 +251,7 @@ class HeaderControlState extends State { ListTile( dense: true, title: const Text('CDN 设置', style: titleStyle), - leading: Icon(MdiIcons.cloudPlusOutline, size: 20), + leading: const Icon(MdiIcons.cloudPlusOutline, size: 20), subtitle: Text( '当前:${CDNService.fromCode(defaultCDNService).description},无法播放请切换', style: subTitleStyle, @@ -2068,7 +2068,7 @@ class HeaderControlState extends State { ), onPressed: () => videoDetailCtr.showSBDetail(context), - icon: Icon( + icon: const Icon( MdiIcons.advertisements, size: 19, color: Colors.white, diff --git a/lib/pages/whisper/view.dart b/lib/pages/whisper/view.dart index a9def3f95..e03de5fa3 100644 --- a/lib/pages/whisper/view.dart +++ b/lib/pages/whisper/view.dart @@ -38,7 +38,7 @@ class _WhisperPageState extends State { controller: _controller, ); }, - icon: Icon(size: 20, e.type.icon), + icon: e.type.icon, ); }).toList()); } @@ -58,7 +58,7 @@ class _WhisperPageState extends State { }, child: Row( children: [ - Icon(size: 17, e.type.icon), + e.type.icon, Text(' ${e.title}'), ], ), diff --git a/lib/pages/whisper_secondary/view.dart b/lib/pages/whisper_secondary/view.dart index c6ffe4f79..5967ae78b 100644 --- a/lib/pages/whisper_secondary/view.dart +++ b/lib/pages/whisper_secondary/view.dart @@ -49,7 +49,7 @@ class _WhisperSecPageState extends State { }, child: Row( children: [ - Icon(size: 17, e.type.icon), + e.type.icon, Text(' ${e.title}'), ], ), diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index 58f26d130..5e8e78e48 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -450,7 +450,7 @@ class _PLVideoPlayerState extends State child: ComBtn( icon: Transform.rotate( angle: pi / 2, - child: Icon( + child: const Icon( MdiIcons.viewHeadline, semanticLabel: '分段信息', size: 22, diff --git a/lib/utils/extension.dart b/lib/utils/extension.dart index 08aca2c63..d823dee58 100644 --- a/lib/utils/extension.dart +++ b/lib/utils/extension.dart @@ -132,18 +132,20 @@ extension RationalExt on Rational { } extension ThreeDotItemTypeExt on ThreeDotItemType { - IconData get icon => switch (this) { - ThreeDotItemType.THREE_DOT_ITEM_TYPE_MSG_SETTING => Icons.settings, + Icon get icon => switch (this) { + ThreeDotItemType.THREE_DOT_ITEM_TYPE_MSG_SETTING => + const Icon(Icons.settings, size: 20), ThreeDotItemType.THREE_DOT_ITEM_TYPE_READ_ALL => - Icons.cleaning_services, + const Icon(Icons.cleaning_services, size: 20), ThreeDotItemType.THREE_DOT_ITEM_TYPE_CLEAR_LIST => - Icons.delete_forever_outlined, - ThreeDotItemType.THREE_DOT_ITEM_TYPE_UP_HELPER => Icons.live_tv, + const Icon(Icons.delete_forever_outlined, size: 20), + ThreeDotItemType.THREE_DOT_ITEM_TYPE_UP_HELPER => + const Icon(Icons.live_tv, size: 20), ThreeDotItemType.THREE_DOT_ITEM_TYPE_CONTACTS => - Icons.account_box_outlined, + const Icon(Icons.account_box_outlined, size: 20), ThreeDotItemType.THREE_DOT_ITEM_TYPE_FANS_GROUP_HELPER => - Icons.notifications_none, - _ => MdiIcons.circleMedium, + const Icon(Icons.notifications_none, size: 20), + _ => const Icon(MdiIcons.circleMedium, size: 20), }; void action({ diff --git a/pubspec.lock b/pubspec.lock index f8565fb55..45203fbbf 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1155,11 +1155,12 @@ packages: material_design_icons_flutter: dependency: "direct main" description: - name: material_design_icons_flutter - sha256: "6f986b7a51f3ad4c00e33c5c84e8de1bdd140489bbcdc8b66fc1283dad4dea5a" - url: "https://pub.dev" - source: hosted - version: "7.0.7296" + path: "." + ref: const + resolved-ref: c11f18c031f1045900dc3e817f7519eb863c2550 + url: "https://github.com/bggRGjQaUbCoE/material_design_icons_flutter.git" + source: git + version: "7.0.7447" media_kit: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 507c5c950..bf247a6b7 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -90,7 +90,11 @@ dependencies: # pull_to_refresh_notification: ^3.1.0 # 图标 font_awesome_flutter: ^10.8.0 - material_design_icons_flutter: ^7.0.7296 + # material_design_icons_flutter: ^7.0.7296 + material_design_icons_flutter: + git: + url: https://github.com/bggRGjQaUbCoE/material_design_icons_flutter.git + ref: const # toast flutter_smart_dialog: ^4.9.8+5 # 下滑关闭