feat(ui): 校正首页发现详情播放器预览

This commit is contained in:
Zhou Weidong committed 2026-10-19 15:50:11 +08:00
1 parent 87db548591
commit 21d457fd98
13 files changed
+1218 -1684

No files matched your search

+160 -394
View File
@@ -3,27 +3,21 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:video_player/video_player.dart';
import '../../../core/data/models/drama.dart';
import '../../../core/formatting/localized_content.dart';
import '../../../core/theme/app_colors.dart';
import '../../../core/theme/app_dimens.dart';
import '../../../core/widgets/glass_panel.dart';
import '../../../core/widgets/remote_image.dart';
import '../../../l10n/app_localizations.dart';
import 'seek_progress_bar.dart';
/// Immersive home layer from `screens/01-home.html`.
///
/// Playback, ticket, progress, favorite, like, comment, and share callbacks are
/// all owned by [PlayerPage]; this widget only paints the design overlay.
/// Home feed layer from `docs/移动端设计预览.html` screen 01.
class HomeFeedOverlay extends StatelessWidget {
const HomeFeedOverlay({
super.key,
required this.l10n,
required this.drama,
required this.episode,
required this.controller,
required this.liked,
required this.saved,
required this.onLike,
@@ -34,15 +28,12 @@ class HomeFeedOverlay extends StatelessWidget {
required this.onDownload,
required this.onClear,
required this.onSearch,
required this.onScrubStart,
required this.onSeek,
this.onPip,
});
final AppLocalizations l10n;
final Drama drama;
final Episode episode;
final VideoPlayerController? controller;
final bool liked;
final bool saved;
final VoidCallback onLike;
@@ -54,12 +45,9 @@ class HomeFeedOverlay extends StatelessWidget {
final VoidCallback onClear;
final VoidCallback onSearch;
final VoidCallback? onPip;
final VoidCallback? onScrubStart;
final Future<void> Function(Duration)? onSeek;
@override
Widget build(BuildContext context) {
final compact = MediaQuery.sizeOf(context).height < 620;
return SafeArea(
bottom: false,
child: Column(
@@ -67,16 +55,16 @@ class HomeFeedOverlay extends StatelessWidget {
_header(context),
Expanded(
child: Align(
alignment: Alignment.bottomRight,
alignment: Alignment.centerRight,
child: Padding(
padding: const EdgeInsets.fromLTRB(0, 0, 12, 4),
child: _rail(compact),
padding: const EdgeInsets.fromLTRB(0, 0, 10, 12),
child: _rail(),
),
),
),
Padding(
padding: const EdgeInsets.fromLTRB(12, 0, 12, 10),
child: _infoCard(context, compact),
padding: const EdgeInsets.fromLTRB(14, 0, 14, 14),
child: _infoCard(),
),
],
),
@@ -85,79 +73,35 @@ class HomeFeedOverlay extends StatelessWidget {
Widget _header(BuildContext context) {
return Padding(
padding: const EdgeInsets.fromLTRB(16, 6, 12, 4),
padding: const EdgeInsets.fromLTRB(14, 6, 10, 4),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
width: 8,
height: 8,
decoration: const BoxDecoration(
color: AppColors.blue,
shape: BoxShape.circle,
),
),
const SizedBox(width: 7),
Flexible(
child: Text(
localizedDramaTitle(l10n, drama),
key: const Key('home-feed-title'),
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
fontFamily: 'Outfit',
color: AppColors.onSurface,
fontSize: 17,
fontWeight: FontWeight.w700,
height: 1.1,
shadows: [
Shadow(blurRadius: 10, color: Colors.black87),
],
),
),
),
if (drama.isHot) ...[const SizedBox(width: 6), _hotBadge()],
],
),
const SizedBox(height: 3),
Text(
'${l10n.seasonOneVerticalCinema} · ${l10n.nowPlaying}',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: Colors.white.withValues(alpha: 0.64),
fontSize: 10.5,
fontWeight: FontWeight.w500,
shadows: const [
Shadow(blurRadius: 8, color: Colors.black87),
],
),
),
],
child: Text(
localizedDramaTitle(l10n, drama),
key: const Key('home-feed-title'),
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
fontFamily: 'Outfit',
color: AppColors.onSurface,
fontSize: 13,
fontWeight: FontWeight.w800,
shadows: [Shadow(blurRadius: 8, color: Colors.black)],
),
),
),
const SizedBox(width: 6),
_GlassIconButton(
_RoundIcon(
key: const Key('home-feed-search'),
tooltip: l10n.search,
icon: Icons.search_rounded,
onPressed: onSearch,
),
const SizedBox(width: 4),
_GlassIconButton(
tooltip: l10n.clearScreen,
icon: Icons.fit_screen_outlined,
onPressed: onClear,
),
const SizedBox(width: 4),
_GlassIconButton(
const SizedBox(width: 6),
_RoundIcon(
key: const Key('home-feed-more'),
tooltip: l10n.more,
icon: Icons.more_horiz_rounded,
icon: Icons.menu_rounded,
onPressed: () => _openMore(context),
),
],
@@ -165,131 +109,53 @@ class HomeFeedOverlay extends StatelessWidget {
);
}
Widget _hotBadge() {
return DecoratedBox(
decoration: BoxDecoration(
color: AppColors.blue.withValues(alpha: 0.18),
borderRadius: BorderRadius.circular(5),
border: Border.all(color: AppColors.blue.withValues(alpha: 0.4)),
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 1),
child: Text(
l10n.hot,
style: const TextStyle(
color: AppColors.blue,
fontSize: 8,
fontWeight: FontWeight.w800,
letterSpacing: 0.4,
height: 1.1,
),
),
),
);
}
Widget _rail(bool compact) {
Widget _rail() {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
Semantics(
button: true,
label: saved ? l10n.followingTab : l10n.followSeries,
child: InkWell(
key: const Key('home-feed-follow'),
onTap: onSave,
customBorder: const CircleBorder(),
child: Stack(
clipBehavior: Clip.none,
children: [
Container(
width: compact ? 40 : 44,
height: compact ? 40 : 44,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(color: AppColors.blue, width: 2),
color: AppColors.raised,
),
child: ClipOval(
child: RemoteImage(
drama.posterUrl,
fit: BoxFit.cover,
showFallbackIcon: true,
),
),
),
Positioned(
bottom: -4,
left: 14,
child: Container(
width: 16,
height: 16,
decoration: BoxDecoration(
color: saved ? AppColors.success : AppColors.blue,
shape: BoxShape.circle,
border: Border.all(color: AppColors.ink, width: 2),
),
child: Icon(
saved ? Icons.check_rounded : Icons.add_rounded,
color: AppColors.onPrimary,
size: 11,
),
),
),
],
),
),
),
const SizedBox(height: 8),
_RailAction(
compact: compact,
_RailItem(
key: const Key('rail-like'),
icon: liked ? Icons.favorite_rounded : Icons.favorite_border_rounded,
label: l10n.like,
selected: liked,
onTap: onLike,
),
_RailAction(
compact: compact,
key: const Key('rail-save'),
icon: saved ? Icons.bookmark_rounded : Icons.bookmark_border_rounded,
label: l10n.save,
selected: saved,
onTap: onSave,
),
_RailAction(
compact: compact,
_RailItem(
key: const Key('rail-comments'),
icon: Icons.chat_bubble_outline_rounded,
label: l10n.comments,
onTap: onComments,
),
_RailAction(
compact: compact,
_RailItem(
key: const Key('rail-share'),
icon: Icons.share_outlined,
icon: Icons.ios_share_rounded,
label: l10n.share,
onTap: onShare,
),
_RailItem(
key: const Key('rail-save'),
icon: saved ? Icons.star_rounded : Icons.star_border_rounded,
label: l10n.favorite,
selected: saved,
onTap: onSave,
),
],
);
}
Widget _infoCard(BuildContext context, bool compact) {
final genres = [
if (drama.genre.isNotEmpty) drama.genre,
if (drama.category.isNotEmpty) drama.category,
].take(2).toList();
Widget _infoCard() {
final genre = drama.genre.isEmpty ? drama.category : drama.genre;
return GlassPanel(
key: const Key('home-feed-info-card'),
radius: 26,
blur: 24,
padding: const EdgeInsets.fromLTRB(16, 13, 16, 12),
radius: 14,
blur: 6,
padding: const EdgeInsets.fromLTRB(12, 11, 12, 11),
gradient: const LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [Color(0x260B0C10), Color(0x590B0C10)],
colors: [Color(0xBF0F0F14), Color(0xA60F0F14)],
),
borderColor: Colors.white.withValues(alpha: 0.06),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
@@ -298,94 +164,73 @@ class HomeFeedOverlay extends StatelessWidget {
children: [
Expanded(
child: Wrap(
spacing: 5,
spacing: 6,
runSpacing: 4,
children: [
for (var i = 0; i < genres.length; i++)
_genreChip(genres[i], i.isEven),
if (genre.isNotEmpty)
_tag(
genre,
background: AppColors.blue,
foreground: AppColors.onPrimary,
),
if (drama.category.isNotEmpty && drama.category != genre)
_tag(
drama.category,
background: const Color(0xFF333333),
foreground: const Color(0xFFDDDDDD),
),
],
),
),
const SizedBox(width: 6),
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3),
decoration: BoxDecoration(
color: Colors.white.withValues(alpha: 0.06),
borderRadius: BorderRadius.circular(999),
border: Border.all(color: AppColors.border),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const SizedBox(
width: 5,
height: 5,
child: DecoratedBox(
decoration: BoxDecoration(
color: AppColors.success,
shape: BoxShape.circle,
),
),
),
const SizedBox(width: 4),
Text(
'${l10n.episodeLabel(episode.number)} / '
'${drama.episodeCount}',
key: const Key('home-feed-episode'),
style: const TextStyle(
color: AppColors.cream,
fontSize: 10,
fontWeight: FontWeight.w600,
),
),
],
Flexible(
child: Text(
l10n.episodeOfTotal(episode.number, drama.episodeCount),
key: const Key('home-feed-episode'),
maxLines: 1,
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.right,
style: const TextStyle(color: Color(0xFFBBBBBB), fontSize: 9),
),
),
],
),
const SizedBox(height: 8),
const SizedBox(height: 7),
Text(
drama.synopsis,
maxLines: compact ? 1 : 2,
localizedEpisodeTitle(l10n, episode.number),
key: const Key('home-feed-episode-title'),
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
color: AppColors.cream,
fontSize: 12,
height: 1.35,
color: AppColors.onSurface,
fontSize: 13,
fontWeight: FontWeight.w800,
),
),
const SizedBox(height: 6),
SeekProgressBar(
valueListenable: controller,
semanticsLabel: l10n.seekVideo,
showTimes: false,
compact: true,
onScrubStart: onScrubStart,
onSeek: onSeek,
),
_timeRow(),
const SizedBox(height: 8),
const SizedBox(height: 10),
Row(
children: [
Expanded(
child: SizedBox(
height: 40,
height: 34,
child: FilledButton.icon(
key: const Key('home-feed-follow-series'),
onPressed: onSave,
style: FilledButton.styleFrom(
backgroundColor: AppColors.blue,
foregroundColor: AppColors.onPrimary,
shape: const StadiumBorder(),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
AppDimens.buttonRadius,
),
),
padding: EdgeInsets.zero,
textStyle: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.w700,
),
),
icon: Icon(
saved ? Icons.check_rounded : Icons.add_rounded,
size: 16,
),
icon: const Icon(Icons.play_arrow_rounded, size: 16),
label: Text(
saved ? l10n.followingTab : l10n.followSeries,
maxLines: 1,
@@ -395,11 +240,33 @@ class HomeFeedOverlay extends StatelessWidget {
),
),
const SizedBox(width: 8),
_GlassAction(
tooltip: l10n.episodes,
icon: Icons.grid_view_rounded,
label: l10n.episodes,
onPressed: onEpisodes,
Expanded(
child: SizedBox(
height: 34,
child: FilledButton(
key: const Key('home-feed-episodes'),
onPressed: onEpisodes,
style: FilledButton.styleFrom(
backgroundColor: const Color(0xFF2A2A30),
foregroundColor: AppColors.onSurface,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
AppDimens.buttonRadius,
),
),
padding: EdgeInsets.zero,
textStyle: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
),
),
child: Text(
l10n.episodes,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
),
),
],
),
@@ -408,41 +275,29 @@ class HomeFeedOverlay extends StatelessWidget {
);
}
Widget _genreChip(String label, bool secondary) {
final color = secondary ? AppColors.secondary : AppColors.blue;
Widget _tag(
String label, {
required Color background,
required Color foreground,
}) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
decoration: BoxDecoration(
color: color.withValues(alpha: 0.15),
borderRadius: BorderRadius.circular(999),
border: Border.all(color: color.withValues(alpha: 0.3)),
color: background,
borderRadius: BorderRadius.circular(6),
),
child: Text(
label,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: color,
fontSize: 10,
fontWeight: FontWeight.w600,
height: 1.1,
color: foreground,
fontSize: 8,
fontWeight: FontWeight.w800,
),
),
);
}
Widget _timeRow() {
final source = controller;
if (source == null) {
return const _Times(position: Duration.zero, duration: Duration.zero);
}
return ValueListenableBuilder<VideoPlayerValue>(
valueListenable: source,
builder: (context, value, _) =>
_Times(position: value.position, duration: value.duration),
);
}
Future<void> _openMore(BuildContext context) async {
await showModalBottomSheet<void>(
context: context,
@@ -456,7 +311,7 @@ class HomeFeedOverlay extends StatelessWidget {
key: const Key('home-feed-download'),
leading: const Icon(
Icons.download_outlined,
color: AppColors.blueSoft,
color: AppColors.secondary,
),
title: Text(l10n.downloads),
onTap: () {
@@ -469,7 +324,7 @@ class HomeFeedOverlay extends StatelessWidget {
key: const Key('home-feed-pip'),
leading: const Icon(
Icons.picture_in_picture_alt_outlined,
color: AppColors.blueSoft,
color: AppColors.secondary,
),
title: Text(l10n.pictureInPicture),
onTap: () {
@@ -478,9 +333,10 @@ class HomeFeedOverlay extends StatelessWidget {
},
),
ListTile(
key: const Key('home-feed-clear'),
leading: const Icon(
Icons.fit_screen_outlined,
color: AppColors.blueSoft,
color: AppColors.secondary,
),
title: Text(l10n.clearScreen),
onTap: () {
@@ -495,35 +351,9 @@ class HomeFeedOverlay extends StatelessWidget {
}
}
class _Times extends StatelessWidget {
const _Times({required this.position, required this.duration});
final Duration position;
final Duration duration;
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 2),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
_format(position),
style: const TextStyle(color: AppColors.muted, fontSize: 9),
),
Text(
_format(duration),
style: const TextStyle(color: AppColors.muted, fontSize: 9),
),
],
),
);
}
}
class _GlassIconButton extends StatelessWidget {
const _GlassIconButton({
class _RoundIcon extends StatelessWidget {
const _RoundIcon({
super.key,
required this.tooltip,
required this.icon,
required this.onPressed,
@@ -531,140 +361,76 @@ class _GlassIconButton extends StatelessWidget {
final String tooltip;
final IconData icon;
final VoidCallback? onPressed;
final VoidCallback onPressed;
@override
Widget build(BuildContext context) {
return SizedBox(
width: 34,
height: 34,
width: 28,
height: 28,
child: IconButton(
tooltip: tooltip,
onPressed: onPressed,
padding: EdgeInsets.zero,
style: IconButton.styleFrom(
backgroundColor: Colors.black.withValues(alpha: 0.28),
backgroundColor: const Color(0xCC2C2D33),
foregroundColor: AppColors.onSurface,
side: BorderSide(color: Colors.white.withValues(alpha: 0.16)),
),
icon: Icon(icon, size: 18),
icon: Icon(icon, size: 14),
),
);
}
}
class _GlassAction extends StatelessWidget {
const _GlassAction({
required this.tooltip,
required this.icon,
required this.label,
required this.onPressed,
});
final String tooltip;
final IconData icon;
final String label;
final VoidCallback onPressed;
@override
Widget build(BuildContext context) {
return Tooltip(
message: tooltip,
child: InkWell(
onTap: onPressed,
borderRadius: BorderRadius.circular(999),
child: Container(
height: 40,
padding: const EdgeInsets.symmetric(horizontal: 12),
decoration: BoxDecoration(
color: Colors.white.withValues(alpha: 0.10),
borderRadius: BorderRadius.circular(999),
border: Border.all(color: Colors.white.withValues(alpha: 0.15)),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(icon, color: AppColors.secondary, size: 15),
const SizedBox(width: 5),
Text(
label,
maxLines: 1,
style: const TextStyle(
color: AppColors.cream,
fontSize: 11,
fontWeight: FontWeight.w600,
),
),
],
),
),
),
);
}
}
class _RailAction extends StatelessWidget {
const _RailAction({
class _RailItem extends StatelessWidget {
const _RailItem({
super.key,
required this.icon,
required this.label,
required this.onTap,
this.selected = false,
this.compact = false,
});
final IconData icon;
final String label;
final VoidCallback onTap;
final bool selected;
final bool compact;
@override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.only(bottom: compact ? 2 : 3),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
GlassPanel(
radius: 999,
blur: 16,
child: SizedBox(
width: compact ? 40 : 38,
height: compact ? 40 : 38,
child: IconButton(
tooltip: label,
onPressed: onTap,
padding: EdgeInsets.zero,
icon: Icon(
padding: const EdgeInsets.only(bottom: 14),
child: Semantics(
button: true,
label: label,
child: InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(22),
child: SizedBox(
width: 44,
child: Column(
children: [
Icon(
icon,
size: 19,
size: 22,
color: selected ? AppColors.blue : AppColors.onSurface,
),
),
const SizedBox(height: 2),
Text(
label,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
color: AppColors.cream,
fontSize: 9,
shadows: [Shadow(blurRadius: 6, color: Colors.black)],
),
),
],
),
),
if (!compact) ...[
const SizedBox(height: 2),
Text(
label,
maxLines: 1,
style: const TextStyle(
color: AppColors.cream,
fontSize: 9,
fontWeight: FontWeight.w600,
shadows: [Shadow(blurRadius: 6, color: Colors.black)],
),
),
],
],
),
),
);
}
}
String _format(Duration value) {
final minutes = value.inMinutes.remainder(60).toString().padLeft(2, '0');
final seconds = value.inSeconds.remainder(60).toString().padLeft(2, '0');
return '$minutes:$seconds';
}
@@ -0,0 +1,158 @@
// Author: fruit.com.cn
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../../../core/data/models/comment.dart';
import '../../../core/data/models/drama.dart';
import '../../../core/data/repositories/comment_repository.dart';
import '../../../core/data/repositories/interaction_repository.dart';
import '../../../core/data/services/comment_controller.dart';
import '../../../core/theme/app_colors.dart';
import '../../../l10n/app_localizations.dart';
class PlayerFeaturedComment extends StatefulWidget {
const PlayerFeaturedComment({
super.key,
required this.drama,
required this.onViewAll,
});
final Drama drama;
final VoidCallback onViewAll;
@override
State<PlayerFeaturedComment> createState() => _PlayerFeaturedCommentState();
}
class _PlayerFeaturedCommentState extends State<PlayerFeaturedComment> {
late final CommentController _controller;
@override
void initState() {
super.initState();
_controller = CommentController(
comments: context.read<CommentRepository>(),
interactions: context.read<InteractionRepository>(),
dramaId: widget.drama.id,
);
unawaited(_controller.load());
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return ListenableBuilder(
listenable: _controller,
builder: (context, _) {
if (_controller.comments.isEmpty) return const SizedBox.shrink();
return _card(context, _controller.comments.first);
},
);
}
Widget _card(BuildContext context, DramaComment comment) {
final l10n = AppLocalizations.of(context)!;
final time = _time(l10n, comment.createdAt);
return InkWell(
key: const Key('player-featured-comment'),
onTap: widget.onViewAll,
borderRadius: BorderRadius.circular(10),
child: Container(
width: double.infinity,
padding: const EdgeInsets.all(9),
decoration: BoxDecoration(
color: AppColors.card,
borderRadius: BorderRadius.circular(10),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
width: 18,
height: 18,
alignment: Alignment.center,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: AppColors.secondary.withValues(alpha: 0.2),
),
child: Text(
_initial(comment.nickname),
style: const TextStyle(
color: AppColors.onSurface,
fontSize: 8,
fontWeight: FontWeight.w700,
),
),
),
const SizedBox(width: 6),
Expanded(
child: Row(
children: [
Flexible(
child: Text(
comment.nickname.isEmpty
? l10n.localViewer
: comment.nickname,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
color: AppColors.onSurface,
fontSize: 10,
fontWeight: FontWeight.w700,
),
),
),
if (time != null) ...[
const SizedBox(width: 4),
Text(
'· $time',
style: const TextStyle(
color: Color(0xFF777777),
fontSize: 9,
),
),
],
],
),
),
],
),
const SizedBox(height: 5),
Text(
comment.content,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
color: Color(0xFFDDDDDD),
fontSize: 10,
height: 1.5,
),
),
],
),
),
);
}
String? _time(AppLocalizations l10n, DateTime? createdAt) {
if (createdAt == null) return null;
final diff = DateTime.now().difference(createdAt);
if (diff.inMinutes < 1) return null;
return l10n.minutesAgo(diff.inMinutes);
}
String _initial(String nickname) {
final trimmed = nickname.trim();
return trimmed.isEmpty ? '?' : trimmed.substring(0, 1).toUpperCase();
}
}
@@ -129,7 +129,7 @@ class _SeekProgressBarState extends State<SeekProgressBar> {
activeTrackColor: AppColors.blue,
inactiveTrackColor: widget.compact
? Colors.white.withValues(alpha: 0.18)
: Colors.white30,
: const Color(0xFF474248),
thumbColor: widget.compact ? AppColors.blue : Colors.white,
thumbShape: RoundSliderThumbShape(
enabledThumbRadius: widget.compact