diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 745d21c..efe6a60 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -7,7 +7,7 @@ + android:icon="@mipmap/launcher_icon"> >({}); final searchQuery = useState(''); final isSelectionMode = selectedTrackIds.value.isNotEmpty; + final isLargeScreen = MediaQuery.of(context).size.width > 600; + final selectedTab = isLargeScreen ? useState(0) : null; void toggleSelection(int id) { final newSet = Set.from(selectedTrackIds.value); @@ -63,9 +65,8 @@ class LibraryScreen extends HookConsumerWidget { selectedTrackIds.value = {}; } - return DefaultTabController( - length: 3, - child: Scaffold( + if (isLargeScreen) { + return Scaffold( appBar: isSelectionMode ? AppBar( leading: IconButton( @@ -105,13 +106,6 @@ class LibraryScreen extends HookConsumerWidget { : AppBar( centerTitle: true, title: const Text('Library'), - bottom: const TabBar( - tabs: [ - Tab(text: 'Tracks', icon: Icon(Icons.audiotrack)), - Tab(text: 'Albums', icon: Icon(Icons.album)), - Tab(text: 'Playlists', icon: Icon(Icons.queue_music)), - ], - ), actions: [ IconButton( icon: const Icon(Icons.add_circle_outline), @@ -164,224 +158,391 @@ class LibraryScreen extends HookConsumerWidget { const Gap(8), ], ), - body: TabBarView( + body: Row( children: [ - // Tracks Tab (Existing Logic) - StreamBuilder>( - stream: repo.watchAllTracks(), - builder: (context, snapshot) { - if (snapshot.hasError) { - return Center(child: Text('Error: ${snapshot.error}')); - } - if (!snapshot.hasData) { - return const Center(child: CircularProgressIndicator()); - } - final tracks = snapshot.data!; - if (tracks.isEmpty) { - return const Center(child: Text('No tracks yet. Add some!')); - } - - List filteredTracks; - if (searchQuery.value.isEmpty) { - filteredTracks = tracks; - } else { - final query = searchQuery.value.toLowerCase(); - filteredTracks = tracks.where((track) { - if (track.title.toLowerCase().contains(query)) return true; - if (track.artist?.toLowerCase().contains(query) ?? false) - return true; - if (track.album?.toLowerCase().contains(query) ?? false) - return true; - if (track.lyrics != null) { - try { - final lyricsData = LyricsData.fromJsonString( - track.lyrics!, - ); - for (final line in lyricsData.lines) { - if (line.text.toLowerCase().contains(query)) - return true; - } - } catch (e) { - // Ignore parsing errors - } - } - return false; - }).toList(); - } - - if (filteredTracks.isEmpty && searchQuery.value.isNotEmpty) { - return const Center( - child: Text('No tracks match your search.'), - ); - } - - return Column( - children: [ - Padding( - padding: const EdgeInsets.all(16.0), - child: TextField( - onChanged: (value) => searchQuery.value = value, - decoration: const InputDecoration( - hintText: 'Search tracks...', - prefixIcon: Icon(Icons.search), - ), - ), - ), - Expanded( - child: ListView.builder( - padding: EdgeInsets.only( - bottom: 72 + MediaQuery.paddingOf(context).bottom, - ), - itemCount: filteredTracks.length, - itemBuilder: (context, index) { - final track = filteredTracks[index]; - final isSelected = selectedTrackIds.value.contains( - track.id, - ); - - if (isSelectionMode) { - return ListTile( - selected: isSelected, - selectedTileColor: Colors.white10, - leading: Checkbox( - value: isSelected, - onChanged: (_) => toggleSelection(track.id), - ), - title: Text( - track.title, - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - subtitle: Text( - '${track.artist ?? 'Unknown Artist'} • ${_formatDuration(track.duration)}', - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - onTap: () => toggleSelection(track.id), - ); - } - - return Dismissible( - key: Key('track_${track.id}'), - direction: DismissDirection.endToStart, - background: Container( - color: Colors.red, - alignment: Alignment.centerRight, - padding: const EdgeInsets.only(right: 20), - child: const Icon( - Icons.delete, - color: Colors.white, - ), - ), - confirmDismiss: (direction) async { - return await showDialog( - context: context, - builder: (context) { - return AlertDialog( - title: const Text('Delete Track?'), - content: Text( - 'Are you sure you want to delete "${track.title}"? This cannot be undone.', - ), - actions: [ - TextButton( - onPressed: () => - Navigator.of(context).pop(false), - child: const Text('Cancel'), - ), - TextButton( - onPressed: () => - Navigator.of(context).pop(true), - style: TextButton.styleFrom( - foregroundColor: Colors.red, - ), - child: const Text('Delete'), - ), - ], - ); - }, - ); - }, - onDismissed: (direction) { - ref - .read(trackRepositoryProvider.notifier) - .deleteTrack(track.id); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text('Deleted "${track.title}"'), - ), - ); - }, - child: ListTile( - leading: AspectRatio( - aspectRatio: 1, - child: Container( - decoration: BoxDecoration( - color: Colors.grey[800], - borderRadius: BorderRadius.circular(8), - image: track.artUri != null - ? DecorationImage( - image: FileImage( - File(track.artUri!), - ), - fit: BoxFit.cover, - ) - : null, - ), - child: track.artUri == null - ? const Icon( - Icons.music_note, - color: Colors.white54, - ) - : null, - ), - ), - title: Text( - track.title, - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - subtitle: Text( - '${track.artist ?? 'Unknown Artist'} • ${_formatDuration(track.duration)}', - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - trailing: isSelectionMode - ? null - : IconButton( - icon: const Icon(Icons.more_vert), - onPressed: () { - _showTrackOptions(context, ref, track); - }, - ), - onTap: () { - final audio = ref.read(audioHandlerProvider); - audio.playTrack(track); - }, - onLongPress: () { - // Enter selection mode - toggleSelection(track.id); - }, - ), - ); - }, - ), - ), - ], - ); - }, + NavigationRail( + selectedIndex: selectedTab!.value, + onDestinationSelected: (index) => selectedTab.value = index, + destinations: const [ + NavigationRailDestination( + icon: Icon(Icons.audiotrack), + label: Text('Tracks'), + ), + NavigationRailDestination( + icon: Icon(Icons.album), + label: Text('Albums'), + ), + NavigationRailDestination( + icon: Icon(Icons.queue_music), + label: Text('Playlists'), + ), + ], + ), + Expanded( + child: _buildTabContent( + selectedTab.value, + ref, + repo, + selectedTrackIds, + searchQuery, + toggleSelection, + isSelectionMode, + ), ), - - // Albums Tab - const AlbumsTab(), - - // Playlists Tab - const PlaylistsTab(), ], ), - ), + ); + } else { + return DefaultTabController( + length: 3, + child: Scaffold( + appBar: isSelectionMode + ? AppBar( + leading: IconButton( + icon: const Icon(Icons.close), + onPressed: clearSelection, + ), + title: Text('${selectedTrackIds.value.length} selected'), + backgroundColor: Theme.of(context).primaryColorDark, + actions: [ + IconButton( + icon: const Icon(Icons.playlist_add), + tooltip: 'Add to Playlist', + onPressed: () { + _batchAddToPlaylist( + context, + ref, + selectedTrackIds.value.toList(), + clearSelection, + ); + }, + ), + IconButton( + icon: const Icon(Icons.delete), + tooltip: 'Delete', + onPressed: () { + _batchDelete( + context, + ref, + selectedTrackIds.value.toList(), + clearSelection, + ); + }, + ), + const Gap(8), + ], + ) + : AppBar( + centerTitle: true, + title: const Text('Library'), + bottom: const TabBar( + tabs: [ + Tab(text: 'Tracks', icon: Icon(Icons.audiotrack)), + Tab(text: 'Albums', icon: Icon(Icons.album)), + Tab(text: 'Playlists', icon: Icon(Icons.queue_music)), + ], + ), + actions: [ + IconButton( + icon: const Icon(Icons.add_circle_outline), + tooltip: 'Import Files', + onPressed: () async { + final result = await FilePicker.platform.pickFiles( + type: FileType.custom, + allowedExtensions: allAllowedExtensions, + allowMultiple: true, + ); + if (result != null && result.files.isNotEmpty) { + final paths = result.files + .map((f) => f.path) + .whereType() + .toList(); + if (paths.isNotEmpty) { + // Separate audio and lyrics files + final audioPaths = paths.where((path) { + final ext = p + .extension(path) + .toLowerCase() + .replaceFirst('.', ''); + return audioExtensions.contains(ext); + }).toList(); + final lyricsPaths = paths.where((path) { + final ext = p + .extension(path) + .toLowerCase() + .replaceFirst('.', ''); + return lyricsExtensions.contains(ext); + }).toList(); + + // Import tracks if any + if (audioPaths.isNotEmpty) { + await repo.importFiles(audioPaths); + } + + // Import lyrics if any + if (lyricsPaths.isNotEmpty) { + await _batchImportLyricsFromPaths( + context, + ref, + lyricsPaths, + ); + } + } + } + }, + ), + const Gap(8), + ], + ), + body: TabBarView( + children: [ + _buildTracksTab( + ref, + repo, + selectedTrackIds, + searchQuery, + toggleSelection, + isSelectionMode, + ), + const AlbumsTab(), + const PlaylistsTab(), + ], + ), + ), + ); + } + } + + Widget _buildTracksTab( + WidgetRef ref, + dynamic repo, + ValueNotifier> selectedTrackIds, + ValueNotifier searchQuery, + void Function(int) toggleSelection, + bool isSelectionMode, + ) { + return StreamBuilder>( + stream: repo.watchAllTracks(), + builder: (context, snapshot) { + if (snapshot.hasError) { + return Center(child: Text('Error: ${snapshot.error}')); + } + if (!snapshot.hasData) { + return const Center(child: CircularProgressIndicator()); + } + final tracks = snapshot.data!; + if (tracks.isEmpty) { + return const Center(child: Text('No tracks yet. Add some!')); + } + + List filteredTracks; + if (searchQuery.value.isEmpty) { + filteredTracks = tracks; + } else { + final query = searchQuery.value.toLowerCase(); + filteredTracks = tracks.where((track) { + if (track.title.toLowerCase().contains(query)) return true; + if (track.artist?.toLowerCase().contains(query) ?? false) + return true; + if (track.album?.toLowerCase().contains(query) ?? false) + return true; + if (track.lyrics != null) { + try { + final lyricsData = LyricsData.fromJsonString(track.lyrics!); + for (final line in lyricsData.lines) { + if (line.text.toLowerCase().contains(query)) return true; + } + } catch (e) { + // Ignore parsing errors + } + } + return false; + }).toList(); + } + + if (filteredTracks.isEmpty && searchQuery.value.isNotEmpty) { + return const Center(child: Text('No tracks match your search.')); + } + + return Column( + children: [ + Padding( + padding: EdgeInsets.symmetric( + horizontal: MediaQuery.of(context).size.width * 0.04, + vertical: 16.0, + ), + child: TextField( + onChanged: (value) => searchQuery.value = value, + decoration: const InputDecoration( + hintText: 'Search tracks...', + prefixIcon: Icon(Icons.search), + ), + ), + ), + Expanded( + child: ListView.builder( + padding: EdgeInsets.only( + bottom: 72 + MediaQuery.paddingOf(context).bottom, + ), + itemCount: filteredTracks.length, + itemBuilder: (context, index) { + final track = filteredTracks[index]; + final isSelected = selectedTrackIds.value.contains(track.id); + + if (isSelectionMode) { + return ListTile( + selected: isSelected, + selectedTileColor: Colors.white10, + leading: Checkbox( + value: isSelected, + onChanged: (_) => toggleSelection(track.id), + ), + title: Text( + track.title, + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + subtitle: Text( + '${track.artist ?? 'Unknown Artist'} • ${_formatDuration(track.duration)}', + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + onTap: () => toggleSelection(track.id), + ); + } + + return Dismissible( + key: Key('track_${track.id}'), + direction: DismissDirection.endToStart, + background: Container( + color: Colors.red, + alignment: Alignment.centerRight, + padding: const EdgeInsets.only(right: 20), + child: const Icon(Icons.delete, color: Colors.white), + ), + confirmDismiss: (direction) async { + return await showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: const Text('Delete Track?'), + content: Text( + 'Are you sure you want to delete "${track.title}"? This cannot be undone.', + ), + actions: [ + TextButton( + onPressed: () => + Navigator.of(context).pop(false), + child: const Text('Cancel'), + ), + TextButton( + onPressed: () => + Navigator.of(context).pop(true), + style: TextButton.styleFrom( + foregroundColor: Colors.red, + ), + child: const Text('Delete'), + ), + ], + ); + }, + ); + }, + onDismissed: (direction) { + ref + .read(trackRepositoryProvider.notifier) + .deleteTrack(track.id); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Deleted "${track.title}"')), + ); + }, + child: ListTile( + leading: AspectRatio( + aspectRatio: 1, + child: Container( + decoration: BoxDecoration( + color: Colors.grey[800], + borderRadius: BorderRadius.circular(8), + image: track.artUri != null + ? DecorationImage( + image: FileImage(File(track.artUri!)), + fit: BoxFit.cover, + ) + : null, + ), + child: track.artUri == null + ? const Icon( + Icons.music_note, + color: Colors.white54, + ) + : null, + ), + ), + title: Text( + track.title, + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + subtitle: Text( + '${track.artist ?? 'Unknown Artist'} • ${_formatDuration(track.duration)}', + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + trailing: isSelectionMode + ? null + : IconButton( + icon: const Icon(Icons.more_vert), + onPressed: () { + _showTrackOptions(context, ref, track); + }, + ), + onTap: () { + final audio = ref.read(audioHandlerProvider); + audio.playTrack(track); + }, + onLongPress: () { + // Enter selection mode + toggleSelection(track.id); + }, + ), + ); + }, + ), + ), + ], + ); + }, ); } + Widget _buildTabContent( + int index, + WidgetRef ref, + dynamic repo, + ValueNotifier> selectedTrackIds, + ValueNotifier searchQuery, + void Function(int) toggleSelection, + bool isSelectionMode, + ) { + switch (index) { + case 0: + return _buildTracksTab( + ref, + repo, + selectedTrackIds, + searchQuery, + toggleSelection, + isSelectionMode, + ); + case 1: + return const AlbumsTab(); + case 2: + return const PlaylistsTab(); + default: + return const SizedBox(); + } + } + void _showTrackOptions(BuildContext context, WidgetRef ref, Track track) { showModalBottomSheet( context: context, @@ -439,6 +600,7 @@ class LibraryScreen extends HookConsumerWidget { WidgetRef ref, Track track, ) { + final screenSize = MediaQuery.of(context).size; showDialog( context: context, builder: (context) { @@ -448,8 +610,11 @@ class LibraryScreen extends HookConsumerWidget { // Or we can use a Consumer widget inside the dialog. return AlertDialog( title: const Text('Add to Playlist'), - content: SizedBox( - width: double.maxFinite, + content: ConstrainedBox( + constraints: BoxConstraints( + maxWidth: screenSize.width * 0.8, + maxHeight: screenSize.height * 0.6, + ), child: Consumer( builder: (context, ref, child) { final playlistsAsync = ref @@ -509,28 +674,32 @@ class LibraryScreen extends HookConsumerWidget { final titleController = TextEditingController(text: track.title); final artistController = TextEditingController(text: track.artist); final albumController = TextEditingController(text: track.album); + final screenSize = MediaQuery.of(context).size; showDialog( context: context, builder: (context) => AlertDialog( title: const Text('Edit Track'), - content: Column( - spacing: 16, - mainAxisSize: MainAxisSize.min, - children: [ - TextField( - controller: titleController, - decoration: const InputDecoration(labelText: 'Title'), - ), - TextField( - controller: artistController, - decoration: const InputDecoration(labelText: 'Artist'), - ), - TextField( - controller: albumController, - decoration: const InputDecoration(labelText: 'Album'), - ), - ], + content: ConstrainedBox( + constraints: BoxConstraints(maxWidth: screenSize.width * 0.8), + child: Column( + spacing: 16, + mainAxisSize: MainAxisSize.min, + children: [ + TextField( + controller: titleController, + decoration: const InputDecoration(labelText: 'Title'), + ), + TextField( + controller: artistController, + decoration: const InputDecoration(labelText: 'Artist'), + ), + TextField( + controller: albumController, + decoration: const InputDecoration(labelText: 'Album'), + ), + ], + ), ), actions: [ TextButton( @@ -570,13 +739,17 @@ class LibraryScreen extends HookConsumerWidget { List trackIds, VoidCallback onSuccess, ) { + final screenSize = MediaQuery.of(context).size; showDialog( context: context, builder: (context) { return AlertDialog( title: const Text('Add to Playlist'), - content: SizedBox( - width: double.maxFinite, + content: ConstrainedBox( + constraints: BoxConstraints( + maxWidth: screenSize.width * 0.8, + maxHeight: screenSize.height * 0.6, + ), child: Consumer( builder: (context, ref, child) { final playlistsAsync = ref diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json index a2ec33f..96d3fee 100644 --- a/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -1,68 +1,68 @@ { - "images" : [ - { - "size" : "16x16", - "idiom" : "mac", - "filename" : "app_icon_16.png", - "scale" : "1x" + "info": { + "version": 1, + "author": "xcode" }, - { - "size" : "16x16", - "idiom" : "mac", - "filename" : "app_icon_32.png", - "scale" : "2x" - }, - { - "size" : "32x32", - "idiom" : "mac", - "filename" : "app_icon_32.png", - "scale" : "1x" - }, - { - "size" : "32x32", - "idiom" : "mac", - "filename" : "app_icon_64.png", - "scale" : "2x" - }, - { - "size" : "128x128", - "idiom" : "mac", - "filename" : "app_icon_128.png", - "scale" : "1x" - }, - { - "size" : "128x128", - "idiom" : "mac", - "filename" : "app_icon_256.png", - "scale" : "2x" - }, - { - "size" : "256x256", - "idiom" : "mac", - "filename" : "app_icon_256.png", - "scale" : "1x" - }, - { - "size" : "256x256", - "idiom" : "mac", - "filename" : "app_icon_512.png", - "scale" : "2x" - }, - { - "size" : "512x512", - "idiom" : "mac", - "filename" : "app_icon_512.png", - "scale" : "1x" - }, - { - "size" : "512x512", - "idiom" : "mac", - "filename" : "app_icon_1024.png", - "scale" : "2x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} + "images": [ + { + "size": "16x16", + "idiom": "mac", + "filename": "app_icon_16.png", + "scale": "1x" + }, + { + "size": "16x16", + "idiom": "mac", + "filename": "app_icon_32.png", + "scale": "2x" + }, + { + "size": "32x32", + "idiom": "mac", + "filename": "app_icon_32.png", + "scale": "1x" + }, + { + "size": "32x32", + "idiom": "mac", + "filename": "app_icon_64.png", + "scale": "2x" + }, + { + "size": "128x128", + "idiom": "mac", + "filename": "app_icon_128.png", + "scale": "1x" + }, + { + "size": "128x128", + "idiom": "mac", + "filename": "app_icon_256.png", + "scale": "2x" + }, + { + "size": "256x256", + "idiom": "mac", + "filename": "app_icon_256.png", + "scale": "1x" + }, + { + "size": "256x256", + "idiom": "mac", + "filename": "app_icon_512.png", + "scale": "2x" + }, + { + "size": "512x512", + "idiom": "mac", + "filename": "app_icon_512.png", + "scale": "1x" + }, + { + "size": "512x512", + "idiom": "mac", + "filename": "app_icon_1024.png", + "scale": "2x" + } + ] +} \ No newline at end of file diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png index 82b6f9d..9c115ec 100644 Binary files a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png index 13b35eb..3bb707e 100644 Binary files a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png index 0a3f5fa..a02b08a 100644 Binary files a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png index bdb5722..5510638 100644 Binary files a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png index f083318..071deb9 100644 Binary files a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png index 326c0e7..5beab66 100644 Binary files a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png index 2f1632c..088ba3d 100644 Binary files a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png differ diff --git a/pubspec.lock b/pubspec.lock index 4386cd5..5ce230d 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -41,6 +41,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.1" + ansicolor: + dependency: transitive + description: + name: ansicolor + sha256: "50e982d500bc863e1d703448afdbf9e5a72eb48840a4f766fa361ffd6877055f" + url: "https://pub.dev" + source: hosted + version: "2.0.3" archive: dependency: transitive description: @@ -249,6 +257,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.7" + csslib: + dependency: transitive + description: + name: csslib + sha256: "09bad715f418841f976c77db72d5398dc1253c21fb9c0c7f0b0b985860b2d58e" + url: "https://pub.dev" + source: hosted + version: "1.0.2" cupertino_icons: dependency: "direct main" description: @@ -398,6 +414,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.0+1" + flutter_native_splash: + dependency: "direct dev" + description: + name: flutter_native_splash + sha256: "4fb9f4113350d3a80841ce05ebf1976a36de622af7d19aca0ca9a9911c7ff002" + url: "https://pub.dev" + source: hosted + version: "2.4.7" flutter_plugin_android_lifecycle: dependency: transitive description: @@ -472,6 +496,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.3" + html: + dependency: transitive + description: + name: html + sha256: "6d1264f2dffa1b1101c25a91dff0dc2daee4c18e87cd8538729773c073dbf602" + url: "https://pub.dev" + source: hosted + version: "0.15.6" http: dependency: "direct main" description: @@ -1101,6 +1133,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.4.0" + universal_io: + dependency: transitive + description: + name: universal_io + sha256: f63cbc48103236abf48e345e07a03ce5757ea86285ed313a6a032596ed9301e2 + url: "https://pub.dev" + source: hosted + version: "2.3.1" universal_platform: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 7a5e9d2..24bf768 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -68,6 +68,7 @@ dev_dependencies: riverpod_generator: ^3.0.3 drift_dev: ^2.30.0 flutter_launcher_icons: ^0.14.4 + flutter_native_splash: ^2.4.7 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec @@ -113,27 +114,27 @@ flutter: flutter_launcher_icons: android: "launcher_icon" ios: true - image_path: "assets/images/logo.png" + image_path: "assets/images/icon.jpg" min_sdk_android: 21 - image_path_ios_dark_transparent: "assets/images/logo-dark.png" + image_path_ios_dark_transparent: "assets/images/icon-dark.png" remove_alpha_ios: true desaturate_tinted_to_grayscale_ios: true background_color_ios: "#ffffff" web: generate: true - image_path: "assets/images/logo-dark.png" + image_path: "assets/images/icon-dark.png" background_color: "#ffffff" theme_color: "#2eb0c7" windows: generate: true - image_path: "assets/images/logo-dark.png" + image_path: "assets/images/icon-dark.png" icon_size: 256 macos: generate: true image_path: "assets/images/icon-padded.png" flutter_native_splash: - image: "assets/images/icon.png" - image_dark: "assets/images/icon-dark.png" + image: "assets/images/icon.jpg" + image_dark: "assets/images/icon-dark.jpg" color: "#2eb0c7" color_dark: "#121212" \ No newline at end of file diff --git a/web/favicon.png b/web/favicon.png index 8aaa46a..2e89ec5 100644 Binary files a/web/favicon.png and b/web/favicon.png differ diff --git a/web/icons/Icon-192.png b/web/icons/Icon-192.png index b749bfe..1b8d00c 100644 Binary files a/web/icons/Icon-192.png and b/web/icons/Icon-192.png differ diff --git a/web/icons/Icon-512.png b/web/icons/Icon-512.png index 88cfd48..4141cf1 100644 Binary files a/web/icons/Icon-512.png and b/web/icons/Icon-512.png differ diff --git a/web/icons/Icon-maskable-192.png b/web/icons/Icon-maskable-192.png index eb9b4d7..1b8d00c 100644 Binary files a/web/icons/Icon-maskable-192.png and b/web/icons/Icon-maskable-192.png differ diff --git a/web/icons/Icon-maskable-512.png b/web/icons/Icon-maskable-512.png index d69c566..4141cf1 100644 Binary files a/web/icons/Icon-maskable-512.png and b/web/icons/Icon-maskable-512.png differ diff --git a/web/manifest.json b/web/manifest.json index e673940..409a007 100644 --- a/web/manifest.json +++ b/web/manifest.json @@ -3,8 +3,8 @@ "short_name": "groovybox", "start_url": ".", "display": "standalone", - "background_color": "#0175C2", - "theme_color": "#0175C2", + "background_color": "#ffffff", + "theme_color": "#2eb0c7", "description": "A new Flutter project.", "orientation": "portrait-primary", "prefer_related_applications": false, @@ -32,4 +32,4 @@ "purpose": "maskable" } ] -} +} \ No newline at end of file diff --git a/windows/runner/resources/app_icon.ico b/windows/runner/resources/app_icon.ico index c04e20c..6932cbd 100644 Binary files a/windows/runner/resources/app_icon.ico and b/windows/runner/resources/app_icon.ico differ