fix(flutter): player bar layout, system playlist tap, banner height
Player bar: drop the volume slider — phone hardware controls volume. Right cluster collapses to a single row of shuffle/repeat/queue. Playlist card: GestureDetector defers to children by default, so taps over the cover image area didn't always register. Add HitTestBehavior.opaque so the whole card surface is tappable. Update banner: was 3-4× the height of its text — IconButton's default 48dp tap target plus generous vertical padding dominated. Tighten padding (8→4), shrink dismiss button to 32×32 with iconSize 16, slim the action button to 28dp height with shrinkWrap tap target. Home screen: ClampingScrollPhysics + 140dp bottom padding (already applied in working tree) so scroll stops just above the mini-player.
This commit is contained in:
@@ -58,20 +58,30 @@ class HomeScreen extends ConsumerWidget {
|
|||||||
),
|
),
|
||||||
data: (h) => RefreshIndicator(
|
data: (h) => RefreshIndicator(
|
||||||
onRefresh: () async => ref.refresh(homeProvider.future),
|
onRefresh: () async => ref.refresh(homeProvider.future),
|
||||||
child: ListView(children: [
|
child: ListView(
|
||||||
_PlaylistsSection(
|
// ClampingScrollPhysics: no bounce overscroll past the
|
||||||
playlists: allPlaylists.value?.owned ?? const [],
|
// content. Combined with the bottom padding below, this
|
||||||
status: status.value ?? SystemPlaylistsStatus.empty(),
|
// makes "scroll to end" land the last item just above the
|
||||||
),
|
// player bar — no empty void.
|
||||||
_RecentlyAddedSection(albums: h.recentlyAddedAlbums),
|
physics: const ClampingScrollPhysics(),
|
||||||
_RediscoverSection(
|
children: [
|
||||||
albums: h.rediscoverAlbums,
|
_PlaylistsSection(
|
||||||
artists: h.rediscoverArtists,
|
playlists: allPlaylists.value?.owned ?? const [],
|
||||||
),
|
status: status.value ?? SystemPlaylistsStatus.empty(),
|
||||||
_MostPlayedSection(tracks: h.mostPlayedTracks),
|
),
|
||||||
_LastPlayedSection(artists: h.lastPlayedArtists),
|
_RecentlyAddedSection(albums: h.recentlyAddedAlbums),
|
||||||
const SizedBox(height: 96),
|
_RediscoverSection(
|
||||||
]),
|
albums: h.rediscoverAlbums,
|
||||||
|
artists: h.rediscoverArtists,
|
||||||
|
),
|
||||||
|
_MostPlayedSection(tracks: h.mostPlayedTracks),
|
||||||
|
_LastPlayedSection(artists: h.lastPlayedArtists),
|
||||||
|
// Bottom padding ≈ player bar height (top row 80 + seek
|
||||||
|
// 30 + padding 16 ≈ ~140) so the last section's bottom
|
||||||
|
// edge lands right above the player when scrolled.
|
||||||
|
const SizedBox(height: 140),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -10,19 +10,20 @@ import '../shared/widgets/track_actions/track_actions_button.dart';
|
|||||||
import '../theme/theme_extension.dart';
|
import '../theme/theme_extension.dart';
|
||||||
import 'player_provider.dart';
|
import 'player_provider.dart';
|
||||||
|
|
||||||
/// Compact player bar mounted in the app shell. Layout mirrors the
|
/// Compact player bar mounted in the app shell. Layout:
|
||||||
/// web's compact PlayerBar (#358):
|
|
||||||
///
|
///
|
||||||
/// ┌──────────┬───────────┬──────────────────┐
|
/// ┌────────────────────────────┬────────┬─────────┐
|
||||||
/// │ art │ ♥ ⋮ │ 🔀 🔁 ☰ │ ← short row
|
/// │ [art] Title ♥ ⋮ │ ⏮ ⏯ ⏭ │ 🔀 🔁 ☰ │
|
||||||
/// │ title │ │ │
|
/// │ Artist │ │ │
|
||||||
/// │ artist │ ⏮ ⏯ ⏭ │ volume slider │ ← play controls
|
/// ├────────────────────────────┴────────┴─────────┤
|
||||||
/// ├──────────┴───────────┴──────────────────┤
|
/// │ 0:00 ━━●━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 3:06 │
|
||||||
/// │ 0:42 ━━━━━●━━━━━━━━━━━━━━━━━━━━━ 3:21 │
|
/// └────────────────────────────────────────────────┘
|
||||||
/// └─────────────────────────────────────────┘
|
|
||||||
///
|
///
|
||||||
/// Track-info column (cover + title block) tap target opens
|
/// Heart + kebab sit inline at the right of the title (per operator
|
||||||
/// NowPlayingScreen.
|
/// "directly to the right of the track name"). Play controls stay
|
||||||
|
/// full-size (40/48/40). Right column: shuffle/repeat/queue. Volume
|
||||||
|
/// is handled by the phone's hardware buttons. Bottom: full-width
|
||||||
|
/// seek + time labels.
|
||||||
class PlayerBar extends ConsumerWidget {
|
class PlayerBar extends ConsumerWidget {
|
||||||
const PlayerBar({super.key});
|
const PlayerBar({super.key});
|
||||||
|
|
||||||
@@ -43,21 +44,16 @@ class PlayerBar extends ConsumerWidget {
|
|||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
|
// Top row: track info (with inline like+kebab) | play controls | right cluster
|
||||||
IntrinsicHeight(
|
IntrinsicHeight(
|
||||||
child: Row(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(child: _TrackInfoColumn(media: media)),
|
||||||
flex: 35,
|
|
||||||
child: _TrackInfoColumn(media: media),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
_MiddleColumn(media: media, playback: playback, ref: ref),
|
_PlayControls(playback: playback, ref: ref),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
Expanded(
|
_RightCluster(playback: playback),
|
||||||
flex: 30,
|
|
||||||
child: _RightColumn(playback: playback),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -77,6 +73,8 @@ class _TrackInfoColumn extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final fs = Theme.of(context).extension<FabledSwordTheme>()!;
|
final fs = Theme.of(context).extension<FabledSwordTheme>()!;
|
||||||
|
final artistName = (media.artist ?? '').trim();
|
||||||
|
|
||||||
return InkWell(
|
return InkWell(
|
||||||
onTap: () => context.push('/now-playing'),
|
onTap: () => context.push('/now-playing'),
|
||||||
child: Row(
|
child: Row(
|
||||||
@@ -99,18 +97,33 @@ class _TrackInfoColumn extends StatelessWidget {
|
|||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
// Title row: title text + inline like + kebab
|
||||||
media.title,
|
Row(
|
||||||
maxLines: 1,
|
mainAxisSize: MainAxisSize.max,
|
||||||
overflow: TextOverflow.ellipsis,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
style: TextStyle(color: fs.parchment, fontSize: 14),
|
children: [
|
||||||
),
|
Expanded(
|
||||||
Text(
|
child: Text(
|
||||||
media.artist ?? '',
|
media.title,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: TextStyle(color: fs.ash, fontSize: 12),
|
style: TextStyle(color: fs.parchment, fontSize: 14),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
LikeButton(kind: LikeKind.track, id: media.id, size: 18),
|
||||||
|
TrackActionsButton(
|
||||||
|
track: _trackRefFromMediaItem(media),
|
||||||
|
hideQueueActions: true,
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
|
if (artistName.isNotEmpty)
|
||||||
|
Text(
|
||||||
|
artistName,
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: TextStyle(color: fs.ash, fontSize: 12),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -120,13 +133,8 @@ class _TrackInfoColumn extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _MiddleColumn extends StatelessWidget {
|
class _PlayControls extends StatelessWidget {
|
||||||
const _MiddleColumn({
|
const _PlayControls({required this.playback, required this.ref});
|
||||||
required this.media,
|
|
||||||
required this.playback,
|
|
||||||
required this.ref,
|
|
||||||
});
|
|
||||||
final MediaItem media;
|
|
||||||
final PlaybackState? playback;
|
final PlaybackState? playback;
|
||||||
final WidgetRef ref;
|
final WidgetRef ref;
|
||||||
|
|
||||||
@@ -134,73 +142,54 @@ class _MiddleColumn extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final fs = Theme.of(context).extension<FabledSwordTheme>()!;
|
final fs = Theme.of(context).extension<FabledSwordTheme>()!;
|
||||||
final isPlaying = playback?.playing == true;
|
final isPlaying = playback?.playing == true;
|
||||||
return Column(
|
return Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
children: [
|
children: [
|
||||||
// Top sub-row: like + kebab (shorter than play controls)
|
SizedBox(
|
||||||
Row(
|
width: 40,
|
||||||
mainAxisSize: MainAxisSize.min,
|
height: 40,
|
||||||
children: [
|
child: IconButton(
|
||||||
LikeButton(kind: LikeKind.track, id: media.id, size: 18),
|
padding: EdgeInsets.zero,
|
||||||
TrackActionsButton(
|
icon: Icon(Icons.skip_previous, color: fs.parchment, size: 22),
|
||||||
track: _trackRefFromMediaItem(media),
|
onPressed: () => ref.read(audioHandlerProvider).skipToPrevious(),
|
||||||
hideQueueActions: true,
|
),
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
// Bottom sub-row: play controls — full size 40 / 48 / 40
|
SizedBox(
|
||||||
Row(
|
width: 48,
|
||||||
mainAxisSize: MainAxisSize.min,
|
height: 48,
|
||||||
children: [
|
child: IconButton(
|
||||||
SizedBox(
|
padding: EdgeInsets.zero,
|
||||||
width: 40,
|
iconSize: 28,
|
||||||
height: 40,
|
icon: Icon(
|
||||||
child: IconButton(
|
isPlaying ? Icons.pause_circle_filled : Icons.play_circle_filled,
|
||||||
padding: EdgeInsets.zero,
|
color: fs.accent,
|
||||||
icon: Icon(Icons.skip_previous, color: fs.parchment, size: 22),
|
|
||||||
onPressed: () =>
|
|
||||||
ref.read(audioHandlerProvider).skipToPrevious(),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
SizedBox(
|
onPressed: () {
|
||||||
width: 48,
|
final h = ref.read(audioHandlerProvider);
|
||||||
height: 48,
|
if (isPlaying) {
|
||||||
child: IconButton(
|
h.pause();
|
||||||
padding: EdgeInsets.zero,
|
} else {
|
||||||
iconSize: 28,
|
h.play();
|
||||||
icon: Icon(
|
}
|
||||||
isPlaying ? Icons.pause_circle_filled : Icons.play_circle_filled,
|
},
|
||||||
color: fs.accent,
|
),
|
||||||
),
|
),
|
||||||
onPressed: () {
|
SizedBox(
|
||||||
final h = ref.read(audioHandlerProvider);
|
width: 40,
|
||||||
if (isPlaying) {
|
height: 40,
|
||||||
h.pause();
|
child: IconButton(
|
||||||
} else {
|
padding: EdgeInsets.zero,
|
||||||
h.play();
|
icon: Icon(Icons.skip_next, color: fs.parchment, size: 22),
|
||||||
}
|
onPressed: () => ref.read(audioHandlerProvider).skipToNext(),
|
||||||
},
|
),
|
||||||
),
|
|
||||||
),
|
|
||||||
SizedBox(
|
|
||||||
width: 40,
|
|
||||||
height: 40,
|
|
||||||
child: IconButton(
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
icon: Icon(Icons.skip_next, color: fs.parchment, size: 22),
|
|
||||||
onPressed: () => ref.read(audioHandlerProvider).skipToNext(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _RightColumn extends ConsumerWidget {
|
class _RightCluster extends ConsumerWidget {
|
||||||
const _RightColumn({required this.playback});
|
const _RightCluster({required this.playback});
|
||||||
final PlaybackState? playback;
|
final PlaybackState? playback;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -208,96 +197,56 @@ class _RightColumn extends ConsumerWidget {
|
|||||||
final fs = Theme.of(context).extension<FabledSwordTheme>()!;
|
final fs = Theme.of(context).extension<FabledSwordTheme>()!;
|
||||||
final shuffleOn = playback?.shuffleMode == AudioServiceShuffleMode.all;
|
final shuffleOn = playback?.shuffleMode == AudioServiceShuffleMode.all;
|
||||||
final repeatMode = playback?.repeatMode ?? AudioServiceRepeatMode.none;
|
final repeatMode = playback?.repeatMode ?? AudioServiceRepeatMode.none;
|
||||||
final volume = ref.watch(volumeProvider).value ?? 1.0;
|
|
||||||
final actions = ref.read(playerActionsProvider);
|
final actions = ref.read(playerActionsProvider);
|
||||||
|
|
||||||
return Column(
|
return Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
||||||
children: [
|
children: [
|
||||||
// Top sub-row: shuffle / repeat / queue
|
SizedBox(
|
||||||
Row(
|
width: 36,
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
height: 36,
|
||||||
mainAxisSize: MainAxisSize.max,
|
child: IconButton(
|
||||||
children: [
|
padding: EdgeInsets.zero,
|
||||||
SizedBox(
|
tooltip: shuffleOn ? 'Shuffle on' : 'Shuffle off',
|
||||||
width: 32,
|
icon: Icon(
|
||||||
height: 32,
|
Icons.shuffle,
|
||||||
child: IconButton(
|
size: 18,
|
||||||
padding: EdgeInsets.zero,
|
color: shuffleOn ? fs.accent : fs.ash,
|
||||||
tooltip: shuffleOn ? 'Shuffle on' : 'Shuffle off',
|
|
||||||
icon: Icon(
|
|
||||||
Icons.shuffle,
|
|
||||||
size: 18,
|
|
||||||
color: shuffleOn ? fs.accent : fs.ash,
|
|
||||||
),
|
|
||||||
onPressed: actions.toggleShuffle,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
SizedBox(
|
onPressed: actions.toggleShuffle,
|
||||||
width: 32,
|
),
|
||||||
height: 32,
|
|
||||||
child: IconButton(
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
tooltip: switch (repeatMode) {
|
|
||||||
AudioServiceRepeatMode.none => 'Repeat off',
|
|
||||||
AudioServiceRepeatMode.one => 'Repeat one',
|
|
||||||
_ => 'Repeat all',
|
|
||||||
},
|
|
||||||
icon: Icon(
|
|
||||||
repeatMode == AudioServiceRepeatMode.one
|
|
||||||
? Icons.repeat_one
|
|
||||||
: Icons.repeat,
|
|
||||||
size: 18,
|
|
||||||
color: repeatMode == AudioServiceRepeatMode.none
|
|
||||||
? fs.ash
|
|
||||||
: fs.accent,
|
|
||||||
),
|
|
||||||
onPressed: actions.cycleRepeat,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SizedBox(
|
|
||||||
width: 32,
|
|
||||||
height: 32,
|
|
||||||
child: IconButton(
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
tooltip: 'Queue',
|
|
||||||
icon: Icon(Icons.queue_music, size: 18, color: fs.ash),
|
|
||||||
onPressed: () => context.push('/queue'),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
// Bottom sub-row: volume slider
|
SizedBox(
|
||||||
Row(
|
width: 36,
|
||||||
children: [
|
height: 36,
|
||||||
Icon(
|
child: IconButton(
|
||||||
volume == 0
|
padding: EdgeInsets.zero,
|
||||||
? Icons.volume_off
|
tooltip: switch (repeatMode) {
|
||||||
: (volume < 0.5 ? Icons.volume_down : Icons.volume_up),
|
AudioServiceRepeatMode.none => 'Repeat off',
|
||||||
size: 14,
|
AudioServiceRepeatMode.one => 'Repeat one',
|
||||||
color: fs.ash,
|
_ => 'Repeat all',
|
||||||
|
},
|
||||||
|
icon: Icon(
|
||||||
|
repeatMode == AudioServiceRepeatMode.one
|
||||||
|
? Icons.repeat_one
|
||||||
|
: Icons.repeat,
|
||||||
|
size: 18,
|
||||||
|
color: repeatMode == AudioServiceRepeatMode.none
|
||||||
|
? fs.ash
|
||||||
|
: fs.accent,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 4),
|
onPressed: actions.cycleRepeat,
|
||||||
Expanded(
|
),
|
||||||
child: SliderTheme(
|
),
|
||||||
data: SliderTheme.of(context).copyWith(
|
SizedBox(
|
||||||
trackHeight: 2,
|
width: 36,
|
||||||
thumbShape: const RoundSliderThumbShape(enabledThumbRadius: 6),
|
height: 36,
|
||||||
overlayShape:
|
child: IconButton(
|
||||||
const RoundSliderOverlayShape(overlayRadius: 12),
|
padding: EdgeInsets.zero,
|
||||||
),
|
tooltip: 'Queue',
|
||||||
child: Slider(
|
icon: Icon(Icons.queue_music, size: 18, color: fs.ash),
|
||||||
activeColor: fs.accent,
|
onPressed: () => context.push('/queue'),
|
||||||
inactiveColor: fs.slate,
|
),
|
||||||
min: 0,
|
|
||||||
max: 1,
|
|
||||||
value: volume.clamp(0.0, 1.0),
|
|
||||||
onChanged: actions.setVolume,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ class PlaylistCard extends StatelessWidget {
|
|||||||
return SizedBox(
|
return SizedBox(
|
||||||
width: 176,
|
width: 176,
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
|
behavior: HitTestBehavior.opaque,
|
||||||
onTap: () => context.push('/playlists/${playlist.id}'),
|
onTap: () => context.push('/playlists/${playlist.id}'),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||||
|
|||||||
@@ -62,11 +62,12 @@ class _UpdateBannerState extends ConsumerState<UpdateBanner> {
|
|||||||
child: SafeArea(
|
child: SafeArea(
|
||||||
bottom: false,
|
bottom: false,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.fromLTRB(12, 8, 4, 8),
|
padding: const EdgeInsets.fromLTRB(12, 4, 4, 4),
|
||||||
child: Row(
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Icon(Icons.system_update, color: fs.parchment, size: 18),
|
Icon(Icons.system_update, color: fs.parchment, size: 16),
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 8),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@@ -76,11 +77,11 @@ class _UpdateBannerState extends ConsumerState<UpdateBanner> {
|
|||||||
_stage == _Stage.error
|
_stage == _Stage.error
|
||||||
? 'Update failed'
|
? 'Update failed'
|
||||||
: 'Update Minstrel · ${info.version} available',
|
: 'Update Minstrel · ${info.version} available',
|
||||||
style: TextStyle(color: fs.parchment, fontSize: 13),
|
style: TextStyle(color: fs.parchment, fontSize: 12),
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
if (_stage == _Stage.downloading) ...[
|
if (_stage == _Stage.downloading) ...[
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 2),
|
||||||
LinearProgressIndicator(
|
LinearProgressIndicator(
|
||||||
value: _progress > 0 ? _progress : null,
|
value: _progress > 0 ? _progress : null,
|
||||||
minHeight: 2,
|
minHeight: 2,
|
||||||
@@ -89,11 +90,11 @@ class _UpdateBannerState extends ConsumerState<UpdateBanner> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
if (_stage == _Stage.error && _error != null) ...[
|
if (_stage == _Stage.error && _error != null) ...[
|
||||||
const SizedBox(height: 2),
|
const SizedBox(height: 1),
|
||||||
Text(
|
Text(
|
||||||
_error!,
|
_error!,
|
||||||
style: TextStyle(color: fs.ash, fontSize: 11),
|
style: TextStyle(color: fs.ash, fontSize: 11),
|
||||||
maxLines: 2,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -105,20 +106,35 @@ class _UpdateBannerState extends ConsumerState<UpdateBanner> {
|
|||||||
onPressed: () => _onInstall(info),
|
onPressed: () => _onInstall(info),
|
||||||
style: TextButton.styleFrom(
|
style: TextButton.styleFrom(
|
||||||
foregroundColor: fs.accent,
|
foregroundColor: fs.accent,
|
||||||
minimumSize: const Size(64, 36),
|
minimumSize: const Size(56, 28),
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||||
|
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
|
visualDensity: VisualDensity.compact,
|
||||||
),
|
),
|
||||||
child: const Text('Install'),
|
child: const Text('Install', style: TextStyle(fontSize: 13)),
|
||||||
),
|
),
|
||||||
if (_stage == _Stage.error)
|
if (_stage == _Stage.error)
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => _onInstall(info),
|
onPressed: () => _onInstall(info),
|
||||||
style: TextButton.styleFrom(foregroundColor: fs.accent),
|
style: TextButton.styleFrom(
|
||||||
child: const Text('Retry'),
|
foregroundColor: fs.accent,
|
||||||
|
minimumSize: const Size(56, 28),
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||||
|
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
|
visualDensity: VisualDensity.compact,
|
||||||
|
),
|
||||||
|
child: const Text('Retry', style: TextStyle(fontSize: 13)),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
width: 32,
|
||||||
|
height: 32,
|
||||||
|
child: IconButton(
|
||||||
|
tooltip: 'Dismiss',
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
iconSize: 16,
|
||||||
|
icon: Icon(Icons.close, color: fs.ash),
|
||||||
|
onPressed: () => _onDismiss(info),
|
||||||
),
|
),
|
||||||
IconButton(
|
|
||||||
tooltip: 'Dismiss',
|
|
||||||
icon: Icon(Icons.close, size: 18, color: fs.ash),
|
|
||||||
onPressed: () => _onDismiss(info),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
+209
-1
@@ -73,6 +73,54 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.2"
|
version: "2.1.2"
|
||||||
|
build:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: build
|
||||||
|
sha256: a156715e7cd728130c592f30552575908aae5b100005fbc1f0fb16b3c03a3d10
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.0.6"
|
||||||
|
build_config:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: build_config
|
||||||
|
sha256: "4070d2a59f8eec34c97c86ceb44403834899075f66e8a9d59706f8e7834f6f71"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.3.0"
|
||||||
|
build_daemon:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: build_daemon
|
||||||
|
sha256: bf05f6e12cfea92d3c09308d7bcdab1906cd8a179b023269eed00c071004b957
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.1.1"
|
||||||
|
build_runner:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description:
|
||||||
|
name: build_runner
|
||||||
|
sha256: "1523ce62448ebac2c15a8ba5fbad8acac169788658a7dd2a1c2d9c2a9318b9a6"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.15.0"
|
||||||
|
built_collection:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: built_collection
|
||||||
|
sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "5.1.1"
|
||||||
|
built_value:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: built_value
|
||||||
|
sha256: "34e4067d30ce212937df995f03b69992eea683539ceeac7f679a1f1eba055b56"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "8.12.6"
|
||||||
characters:
|
characters:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -81,6 +129,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.4.1"
|
version: "1.4.1"
|
||||||
|
charcode:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: charcode
|
||||||
|
sha256: fb0f1107cac15a5ea6ef0a6ef71a807b9e4267c713bb93e00e92d737cc8dbd8a
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.4.0"
|
||||||
|
checked_yaml:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: checked_yaml
|
||||||
|
sha256: "959525d3162f249993882720d52b7e0c833978df229be20702b33d48d91de70f"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.4"
|
||||||
cli_config:
|
cli_config:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -89,6 +153,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.0"
|
version: "0.2.0"
|
||||||
|
cli_util:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: cli_util
|
||||||
|
sha256: ff6785f7e9e3c38ac98b2fb035701789de90154024a75b6cb926445e83197d1c
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.4.2"
|
||||||
clock:
|
clock:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -113,6 +185,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.19.1"
|
version: "1.19.1"
|
||||||
|
connectivity_plus:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: connectivity_plus
|
||||||
|
sha256: b5e72753cf63becce2c61fd04dfe0f1c430cc5278b53a1342dc5ad839eab29ec
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "6.1.5"
|
||||||
|
connectivity_plus_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: connectivity_plus_platform_interface
|
||||||
|
sha256: "3c09627c536d22fd24691a905cdd8b14520de69da52c7a97499c8be5284a32ed"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.0"
|
||||||
convert:
|
convert:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -145,6 +233,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.9"
|
version: "1.0.9"
|
||||||
|
dart_style:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: dart_style
|
||||||
|
sha256: "29f7ecc274a86d32920b1d9cfc7502fa87220da41ec60b55f329559d5732e2b2"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.7"
|
||||||
|
dbus:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: dbus
|
||||||
|
sha256: d0c98dcd4f5169878b6cf8f6e0a52403a9dff371a3e2f019697accbf6f44a270
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.7.12"
|
||||||
dio:
|
dio:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -161,6 +265,30 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.2"
|
version: "2.1.2"
|
||||||
|
drift:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: drift
|
||||||
|
sha256: "970cd188fddb111b26ea6a9b07a62bf5c2432d74147b8122c67044ae3b97e99e"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.31.0"
|
||||||
|
drift_dev:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description:
|
||||||
|
name: drift_dev
|
||||||
|
sha256: "917184b2fb867b70a548a83bf0d36268423b38d39968c06cce4905683da49587"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.31.0"
|
||||||
|
drift_flutter:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: drift_flutter
|
||||||
|
sha256: c07120854742a0cae2f7501a0da02493addde550db6641d284983c08762e60a7
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.2.8"
|
||||||
fake_async:
|
fake_async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -320,6 +448,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "8.1.0"
|
version: "8.1.0"
|
||||||
|
graphs:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: graphs
|
||||||
|
sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.2"
|
||||||
hooks:
|
hooks:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -384,6 +520,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.6.7"
|
version: "0.6.7"
|
||||||
|
json_annotation:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: json_annotation
|
||||||
|
sha256: cb09e7dac6210041fad964ed7fbee004f14258b4eca4040f72d1234062ace4c8
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.11.0"
|
||||||
just_audio:
|
just_audio:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -496,6 +640,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.17.6"
|
version: "0.17.6"
|
||||||
|
nm:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: nm
|
||||||
|
sha256: "2c9aae4127bdc8993206464fcc063611e0e36e72018696cd9631023a31b24254"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.5.0"
|
||||||
node_preamble:
|
node_preamble:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -553,7 +705,7 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.0"
|
version: "1.1.0"
|
||||||
path_provider:
|
path_provider:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: path_provider
|
name: path_provider
|
||||||
sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd"
|
sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd"
|
||||||
@@ -640,6 +792,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.0"
|
version: "2.2.0"
|
||||||
|
pubspec_parse:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: pubspec_parse
|
||||||
|
sha256: "0560ba233314abbed0a48a2956f7f022cce7c3e1e73df540277da7544cad4082"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.5.0"
|
||||||
|
recase:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: recase
|
||||||
|
sha256: e4eb4ec2dcdee52dcf99cb4ceabaffc631d7424ee55e56f280bc039737f89213
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.1.0"
|
||||||
record_use:
|
record_use:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -701,6 +869,14 @@ packages:
|
|||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
source_gen:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: source_gen
|
||||||
|
sha256: ec37cc0e6694374cbef59ed79685572c870a54ede6fa30a3e420feb3adffea02
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.2.3"
|
||||||
source_map_stack_trace:
|
source_map_stack_trace:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -765,6 +941,30 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.0"
|
version: "2.4.0"
|
||||||
|
sqlite3:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: sqlite3
|
||||||
|
sha256: "3145bd74dcdb4fd6f5c6dda4d4e4490a8087d7f286a14dee5d37087290f0f8a2"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.9.4"
|
||||||
|
sqlite3_flutter_libs:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: sqlite3_flutter_libs
|
||||||
|
sha256: eeb9e3a45207649076b808f8a5a74d68770d0b7f26ccef6d5f43106eee5375ad
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.5.42"
|
||||||
|
sqlparser:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: sqlparser
|
||||||
|
sha256: "337e9997f7141ffdd054259128553c348635fa318f7ca492f07a4ab76f850d19"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.43.1"
|
||||||
stack_trace:
|
stack_trace:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -789,6 +989,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.4"
|
version: "2.1.4"
|
||||||
|
stream_transform:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: stream_transform
|
||||||
|
sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.1"
|
||||||
string_scanner:
|
string_scanner:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|||||||
Reference in New Issue
Block a user