diff --git a/flutter_client/lib/library/home_screen.dart b/flutter_client/lib/library/home_screen.dart index c714414e..cbf87bd7 100644 --- a/flutter_client/lib/library/home_screen.dart +++ b/flutter_client/lib/library/home_screen.dart @@ -58,20 +58,30 @@ class HomeScreen extends ConsumerWidget { ), data: (h) => RefreshIndicator( onRefresh: () async => ref.refresh(homeProvider.future), - child: ListView(children: [ - _PlaylistsSection( - playlists: allPlaylists.value?.owned ?? const [], - status: status.value ?? SystemPlaylistsStatus.empty(), - ), - _RecentlyAddedSection(albums: h.recentlyAddedAlbums), - _RediscoverSection( - albums: h.rediscoverAlbums, - artists: h.rediscoverArtists, - ), - _MostPlayedSection(tracks: h.mostPlayedTracks), - _LastPlayedSection(artists: h.lastPlayedArtists), - const SizedBox(height: 96), - ]), + child: ListView( + // ClampingScrollPhysics: no bounce overscroll past the + // content. Combined with the bottom padding below, this + // makes "scroll to end" land the last item just above the + // player bar — no empty void. + physics: const ClampingScrollPhysics(), + children: [ + _PlaylistsSection( + playlists: allPlaylists.value?.owned ?? const [], + status: status.value ?? SystemPlaylistsStatus.empty(), + ), + _RecentlyAddedSection(albums: h.recentlyAddedAlbums), + _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), + ], + ), ), ), ), diff --git a/flutter_client/lib/player/player_bar.dart b/flutter_client/lib/player/player_bar.dart index 18feed55..629f8356 100644 --- a/flutter_client/lib/player/player_bar.dart +++ b/flutter_client/lib/player/player_bar.dart @@ -10,19 +10,20 @@ import '../shared/widgets/track_actions/track_actions_button.dart'; import '../theme/theme_extension.dart'; import 'player_provider.dart'; -/// Compact player bar mounted in the app shell. Layout mirrors the -/// web's compact PlayerBar (#358): +/// Compact player bar mounted in the app shell. Layout: /// -/// ┌──────────┬───────────┬──────────────────┐ -/// │ art │ ♥ ⋮ │ 🔀 🔁 ☰ │ ← short row -/// │ title │ │ │ -/// │ artist │ ⏮ ⏯ ⏭ │ volume slider │ ← play controls -/// ├──────────┴───────────┴──────────────────┤ -/// │ 0:42 ━━━━━●━━━━━━━━━━━━━━━━━━━━━ 3:21 │ -/// └─────────────────────────────────────────┘ +/// ┌────────────────────────────┬────────┬─────────┐ +/// │ [art] Title ♥ ⋮ │ ⏮ ⏯ ⏭ │ 🔀 🔁 ☰ │ +/// │ Artist │ │ │ +/// ├────────────────────────────┴────────┴─────────┤ +/// │ 0:00 ━━●━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 3:06 │ +/// └────────────────────────────────────────────────┘ /// -/// Track-info column (cover + title block) tap target opens -/// NowPlayingScreen. +/// Heart + kebab sit inline at the right of the title (per operator +/// "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 { const PlayerBar({super.key}); @@ -43,21 +44,16 @@ class PlayerBar extends ConsumerWidget { child: Column( mainAxisSize: MainAxisSize.min, children: [ + // Top row: track info (with inline like+kebab) | play controls | right cluster IntrinsicHeight( child: Row( - crossAxisAlignment: CrossAxisAlignment.stretch, + crossAxisAlignment: CrossAxisAlignment.center, children: [ - Expanded( - flex: 35, - child: _TrackInfoColumn(media: media), - ), + Expanded(child: _TrackInfoColumn(media: media)), const SizedBox(width: 8), - _MiddleColumn(media: media, playback: playback, ref: ref), + _PlayControls(playback: playback, ref: ref), const SizedBox(width: 8), - Expanded( - flex: 30, - child: _RightColumn(playback: playback), - ), + _RightCluster(playback: playback), ], ), ), @@ -77,6 +73,8 @@ class _TrackInfoColumn extends StatelessWidget { @override Widget build(BuildContext context) { final fs = Theme.of(context).extension()!; + final artistName = (media.artist ?? '').trim(); + return InkWell( onTap: () => context.push('/now-playing'), child: Row( @@ -99,18 +97,33 @@ class _TrackInfoColumn extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.min, children: [ - Text( - media.title, - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: TextStyle(color: fs.parchment, fontSize: 14), - ), - Text( - media.artist ?? '', - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: TextStyle(color: fs.ash, fontSize: 12), + // Title row: title text + inline like + kebab + Row( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + child: Text( + media.title, + maxLines: 1, + overflow: TextOverflow.ellipsis, + 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 { - const _MiddleColumn({ - required this.media, - required this.playback, - required this.ref, - }); - final MediaItem media; +class _PlayControls extends StatelessWidget { + const _PlayControls({required this.playback, required this.ref}); final PlaybackState? playback; final WidgetRef ref; @@ -134,73 +142,54 @@ class _MiddleColumn extends StatelessWidget { Widget build(BuildContext context) { final fs = Theme.of(context).extension()!; final isPlaying = playback?.playing == true; - return Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.center, + return Row( + mainAxisSize: MainAxisSize.min, children: [ - // Top sub-row: like + kebab (shorter than play controls) - Row( - mainAxisSize: MainAxisSize.min, - children: [ - LikeButton(kind: LikeKind.track, id: media.id, size: 18), - TrackActionsButton( - track: _trackRefFromMediaItem(media), - hideQueueActions: true, - ), - ], + SizedBox( + width: 40, + height: 40, + child: IconButton( + padding: EdgeInsets.zero, + icon: Icon(Icons.skip_previous, color: fs.parchment, size: 22), + onPressed: () => ref.read(audioHandlerProvider).skipToPrevious(), + ), ), - // Bottom sub-row: play controls — full size 40 / 48 / 40 - Row( - mainAxisSize: MainAxisSize.min, - children: [ - SizedBox( - width: 40, - height: 40, - child: IconButton( - padding: EdgeInsets.zero, - icon: Icon(Icons.skip_previous, color: fs.parchment, size: 22), - onPressed: () => - ref.read(audioHandlerProvider).skipToPrevious(), - ), + SizedBox( + width: 48, + height: 48, + child: IconButton( + padding: EdgeInsets.zero, + iconSize: 28, + icon: Icon( + isPlaying ? Icons.pause_circle_filled : Icons.play_circle_filled, + color: fs.accent, ), - SizedBox( - width: 48, - height: 48, - child: IconButton( - padding: EdgeInsets.zero, - iconSize: 28, - icon: Icon( - isPlaying ? Icons.pause_circle_filled : Icons.play_circle_filled, - color: fs.accent, - ), - onPressed: () { - final h = ref.read(audioHandlerProvider); - if (isPlaying) { - h.pause(); - } else { - h.play(); - } - }, - ), - ), - 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(), - ), - ), - ], + onPressed: () { + final h = ref.read(audioHandlerProvider); + if (isPlaying) { + h.pause(); + } else { + h.play(); + } + }, + ), + ), + 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 { - const _RightColumn({required this.playback}); +class _RightCluster extends ConsumerWidget { + const _RightCluster({required this.playback}); final PlaybackState? playback; @override @@ -208,96 +197,56 @@ class _RightColumn extends ConsumerWidget { final fs = Theme.of(context).extension()!; final shuffleOn = playback?.shuffleMode == AudioServiceShuffleMode.all; final repeatMode = playback?.repeatMode ?? AudioServiceRepeatMode.none; - final volume = ref.watch(volumeProvider).value ?? 1.0; final actions = ref.read(playerActionsProvider); - return Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.stretch, + return Row( + mainAxisSize: MainAxisSize.min, children: [ - // Top sub-row: shuffle / repeat / queue - Row( - mainAxisAlignment: MainAxisAlignment.end, - mainAxisSize: MainAxisSize.max, - children: [ - SizedBox( - width: 32, - height: 32, - child: IconButton( - padding: EdgeInsets.zero, - tooltip: shuffleOn ? 'Shuffle on' : 'Shuffle off', - icon: Icon( - Icons.shuffle, - size: 18, - color: shuffleOn ? fs.accent : fs.ash, - ), - onPressed: actions.toggleShuffle, - ), + SizedBox( + width: 36, + height: 36, + child: IconButton( + padding: EdgeInsets.zero, + tooltip: shuffleOn ? 'Shuffle on' : 'Shuffle off', + icon: Icon( + Icons.shuffle, + size: 18, + color: shuffleOn ? fs.accent : fs.ash, ), - SizedBox( - 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'), - ), - ), - ], + onPressed: actions.toggleShuffle, + ), ), - // Bottom sub-row: volume slider - Row( - children: [ - Icon( - volume == 0 - ? Icons.volume_off - : (volume < 0.5 ? Icons.volume_down : Icons.volume_up), - size: 14, - color: fs.ash, + SizedBox( + width: 36, + height: 36, + 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, ), - const SizedBox(width: 4), - Expanded( - child: SliderTheme( - data: SliderTheme.of(context).copyWith( - trackHeight: 2, - thumbShape: const RoundSliderThumbShape(enabledThumbRadius: 6), - overlayShape: - const RoundSliderOverlayShape(overlayRadius: 12), - ), - child: Slider( - activeColor: fs.accent, - inactiveColor: fs.slate, - min: 0, - max: 1, - value: volume.clamp(0.0, 1.0), - onChanged: actions.setVolume, - ), - ), - ), - ], + onPressed: actions.cycleRepeat, + ), + ), + SizedBox( + width: 36, + height: 36, + child: IconButton( + padding: EdgeInsets.zero, + tooltip: 'Queue', + icon: Icon(Icons.queue_music, size: 18, color: fs.ash), + onPressed: () => context.push('/queue'), + ), ), ], ); diff --git a/flutter_client/lib/playlists/widgets/playlist_card.dart b/flutter_client/lib/playlists/widgets/playlist_card.dart index f4b5d9da..884f8a72 100644 --- a/flutter_client/lib/playlists/widgets/playlist_card.dart +++ b/flutter_client/lib/playlists/widgets/playlist_card.dart @@ -19,6 +19,7 @@ class PlaylistCard extends StatelessWidget { return SizedBox( width: 176, child: GestureDetector( + behavior: HitTestBehavior.opaque, onTap: () => context.push('/playlists/${playlist.id}'), child: Padding( padding: const EdgeInsets.symmetric(horizontal: 8), diff --git a/flutter_client/lib/update/update_banner.dart b/flutter_client/lib/update/update_banner.dart index 3f2b80df..6605dc28 100644 --- a/flutter_client/lib/update/update_banner.dart +++ b/flutter_client/lib/update/update_banner.dart @@ -62,11 +62,12 @@ class _UpdateBannerState extends ConsumerState { child: SafeArea( bottom: false, child: Padding( - padding: const EdgeInsets.fromLTRB(12, 8, 4, 8), + padding: const EdgeInsets.fromLTRB(12, 4, 4, 4), child: Row( + crossAxisAlignment: CrossAxisAlignment.center, children: [ - Icon(Icons.system_update, color: fs.parchment, size: 18), - const SizedBox(width: 10), + Icon(Icons.system_update, color: fs.parchment, size: 16), + const SizedBox(width: 8), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -76,11 +77,11 @@ class _UpdateBannerState extends ConsumerState { _stage == _Stage.error ? 'Update failed' : 'Update Minstrel · ${info.version} available', - style: TextStyle(color: fs.parchment, fontSize: 13), + style: TextStyle(color: fs.parchment, fontSize: 12), overflow: TextOverflow.ellipsis, ), if (_stage == _Stage.downloading) ...[ - const SizedBox(height: 4), + const SizedBox(height: 2), LinearProgressIndicator( value: _progress > 0 ? _progress : null, minHeight: 2, @@ -89,11 +90,11 @@ class _UpdateBannerState extends ConsumerState { ), ], if (_stage == _Stage.error && _error != null) ...[ - const SizedBox(height: 2), + const SizedBox(height: 1), Text( _error!, style: TextStyle(color: fs.ash, fontSize: 11), - maxLines: 2, + maxLines: 1, overflow: TextOverflow.ellipsis, ), ], @@ -105,20 +106,35 @@ class _UpdateBannerState extends ConsumerState { onPressed: () => _onInstall(info), style: TextButton.styleFrom( 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) TextButton( onPressed: () => _onInstall(info), - style: TextButton.styleFrom(foregroundColor: fs.accent), - child: const Text('Retry'), + style: TextButton.styleFrom( + 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), ), ], ), diff --git a/flutter_client/pubspec.lock b/flutter_client/pubspec.lock index 22e4a936..95704394 100644 --- a/flutter_client/pubspec.lock +++ b/flutter_client/pubspec.lock @@ -73,6 +73,54 @@ packages: url: "https://pub.dev" source: hosted 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: dependency: transitive description: @@ -81,6 +129,22 @@ packages: url: "https://pub.dev" source: hosted 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: dependency: transitive description: @@ -89,6 +153,14 @@ packages: url: "https://pub.dev" source: hosted 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: dependency: transitive description: @@ -113,6 +185,22 @@ packages: url: "https://pub.dev" source: hosted 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: dependency: transitive description: @@ -145,6 +233,22 @@ packages: url: "https://pub.dev" source: hosted 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: dependency: "direct main" description: @@ -161,6 +265,30 @@ packages: url: "https://pub.dev" source: hosted 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: dependency: transitive description: @@ -320,6 +448,14 @@ packages: url: "https://pub.dev" source: hosted version: "8.1.0" + graphs: + dependency: transitive + description: + name: graphs + sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0" + url: "https://pub.dev" + source: hosted + version: "2.3.2" hooks: dependency: transitive description: @@ -384,6 +520,14 @@ packages: url: "https://pub.dev" source: hosted 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: dependency: "direct main" description: @@ -496,6 +640,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.17.6" + nm: + dependency: transitive + description: + name: nm + sha256: "2c9aae4127bdc8993206464fcc063611e0e36e72018696cd9631023a31b24254" + url: "https://pub.dev" + source: hosted + version: "0.5.0" node_preamble: dependency: transitive description: @@ -553,7 +705,7 @@ packages: source: hosted version: "1.1.0" path_provider: - dependency: transitive + dependency: "direct main" description: name: path_provider sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" @@ -640,6 +792,22 @@ packages: url: "https://pub.dev" source: hosted 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: dependency: transitive description: @@ -701,6 +869,14 @@ packages: description: flutter source: sdk 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: dependency: transitive description: @@ -765,6 +941,30 @@ packages: url: "https://pub.dev" source: hosted 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: dependency: transitive description: @@ -789,6 +989,14 @@ packages: url: "https://pub.dev" source: hosted 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: dependency: transitive description: