mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-09-27 20:02:01 +02:00
feat: expose audio duration in metadata API and fix home empty-state race
- Add Duration field to AudioQuality for FLAC (streaminfo) and M4A (mvhd atom) - Expose duration via ReadFileMetadata and extension runtime Go-backend API - Pass duration_ms to extension CheckAvailability for better track matching - Fix home tab showing empty state before extensions finish initializing by keeping the search bar visible with a loading indicator until ready - Refactor hasSearchProvider helper to account for built-in providers - Refine homeEmptyTitle/Subtitle copy (EN + ID translations) - Bump version to 4.5.0+127
This commit is contained in:
@@ -3,8 +3,8 @@ import 'package:flutter/foundation.dart';
|
||||
/// App version and info constants
|
||||
/// Update version here only - all other files will reference this
|
||||
class AppInfo {
|
||||
static const String version = '4.3.1';
|
||||
static const String buildNumber = '126';
|
||||
static const String version = '4.5.0';
|
||||
static const String buildNumber = '127';
|
||||
static const String fullVersion = '$version+$buildNumber';
|
||||
|
||||
/// Shows "Internal" in debug builds, actual version in release.
|
||||
|
||||
@@ -169,13 +169,13 @@ abstract class AppLocalizations {
|
||||
/// Title shown on home when no providers are available yet
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Home is empty'**
|
||||
/// **'No search providers yet'**
|
||||
String get homeEmptyTitle;
|
||||
|
||||
/// Subtitle shown on home when no providers are available yet
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Install your first extension to unlock search and browsing.'**
|
||||
/// **'Install an extension to continue.'**
|
||||
String get homeEmptySubtitle;
|
||||
|
||||
/// Info text about supported URL types
|
||||
|
||||
@@ -30,11 +30,10 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||
String get homeSubtitle => 'Spotify-Link einfügen oder nach Namen suchen';
|
||||
|
||||
@override
|
||||
String get homeEmptyTitle => 'Home is empty';
|
||||
String get homeEmptyTitle => 'No search providers yet';
|
||||
|
||||
@override
|
||||
String get homeEmptySubtitle =>
|
||||
'Install your first extension to unlock search and browsing.';
|
||||
String get homeEmptySubtitle => 'Install an extension to continue.';
|
||||
|
||||
@override
|
||||
String get homeSupports =>
|
||||
|
||||
@@ -30,11 +30,10 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
String get homeSubtitle => 'Paste a supported URL or search by name';
|
||||
|
||||
@override
|
||||
String get homeEmptyTitle => 'Home is empty';
|
||||
String get homeEmptyTitle => 'No search providers yet';
|
||||
|
||||
@override
|
||||
String get homeEmptySubtitle =>
|
||||
'Install your first extension to unlock search and browsing.';
|
||||
String get homeEmptySubtitle => 'Install an extension to continue.';
|
||||
|
||||
@override
|
||||
String get homeSupports => 'Supports: Track, Album, Playlist, Artist URLs';
|
||||
|
||||
@@ -30,11 +30,10 @@ class AppLocalizationsEs extends AppLocalizations {
|
||||
String get homeSubtitle => 'Paste a Spotify link or search by name';
|
||||
|
||||
@override
|
||||
String get homeEmptyTitle => 'Home is empty';
|
||||
String get homeEmptyTitle => 'No search providers yet';
|
||||
|
||||
@override
|
||||
String get homeEmptySubtitle =>
|
||||
'Install your first extension to unlock search and browsing.';
|
||||
String get homeEmptySubtitle => 'Install an extension to continue.';
|
||||
|
||||
@override
|
||||
String get homeSupports => 'Supports: Track, Album, Playlist, Artist URLs';
|
||||
|
||||
@@ -30,11 +30,10 @@ class AppLocalizationsFr extends AppLocalizations {
|
||||
String get homeSubtitle => 'Coller un lien Spotify ou rechercher par nom';
|
||||
|
||||
@override
|
||||
String get homeEmptyTitle => 'Home is empty';
|
||||
String get homeEmptyTitle => 'No search providers yet';
|
||||
|
||||
@override
|
||||
String get homeEmptySubtitle =>
|
||||
'Install your first extension to unlock search and browsing.';
|
||||
String get homeEmptySubtitle => 'Install an extension to continue.';
|
||||
|
||||
@override
|
||||
String get homeSupports => 'Supports: Piste, Album, Playlist, Artiste URLs';
|
||||
|
||||
@@ -30,11 +30,10 @@ class AppLocalizationsHi extends AppLocalizations {
|
||||
String get homeSubtitle => 'Paste a Spotify link or search by name';
|
||||
|
||||
@override
|
||||
String get homeEmptyTitle => 'Home is empty';
|
||||
String get homeEmptyTitle => 'No search providers yet';
|
||||
|
||||
@override
|
||||
String get homeEmptySubtitle =>
|
||||
'Install your first extension to unlock search and browsing.';
|
||||
String get homeEmptySubtitle => 'Install an extension to continue.';
|
||||
|
||||
@override
|
||||
String get homeSupports => 'Supports: Track, Album, Playlist, Artist URLs';
|
||||
|
||||
@@ -31,11 +31,10 @@ class AppLocalizationsId extends AppLocalizations {
|
||||
'Tempel URL yang didukung atau cari berdasarkan nama';
|
||||
|
||||
@override
|
||||
String get homeEmptyTitle => 'Home is empty';
|
||||
String get homeEmptyTitle => 'Belum ada provider pencarian';
|
||||
|
||||
@override
|
||||
String get homeEmptySubtitle =>
|
||||
'Install your first extension to unlock search and browsing.';
|
||||
String get homeEmptySubtitle => 'Pasang ekstensi untuk melanjutkan.';
|
||||
|
||||
@override
|
||||
String get homeSupports => 'Mendukung: URL Track, Album, Playlist, Artis';
|
||||
|
||||
@@ -30,11 +30,10 @@ class AppLocalizationsJa extends AppLocalizations {
|
||||
String get homeSubtitle => 'Spotify のリンクを貼り付けるか、名前で検索します';
|
||||
|
||||
@override
|
||||
String get homeEmptyTitle => 'Home is empty';
|
||||
String get homeEmptyTitle => 'No search providers yet';
|
||||
|
||||
@override
|
||||
String get homeEmptySubtitle =>
|
||||
'Install your first extension to unlock search and browsing.';
|
||||
String get homeEmptySubtitle => 'Install an extension to continue.';
|
||||
|
||||
@override
|
||||
String get homeSupports => 'サポート: トラック、アルバム、プレイリスト、アーティスト、URL';
|
||||
|
||||
@@ -30,11 +30,10 @@ class AppLocalizationsKo extends AppLocalizations {
|
||||
String get homeSubtitle => 'Spotify URL을 붙여 넣거나 검색';
|
||||
|
||||
@override
|
||||
String get homeEmptyTitle => 'Home is empty';
|
||||
String get homeEmptyTitle => 'No search providers yet';
|
||||
|
||||
@override
|
||||
String get homeEmptySubtitle =>
|
||||
'Install your first extension to unlock search and browsing.';
|
||||
String get homeEmptySubtitle => 'Install an extension to continue.';
|
||||
|
||||
@override
|
||||
String get homeSupports => '지원 항목: 트랙, 앨범, 플레이리스트, 아티스트 URLs';
|
||||
|
||||
@@ -30,11 +30,10 @@ class AppLocalizationsNl extends AppLocalizations {
|
||||
String get homeSubtitle => 'Paste a Spotify link or search by name';
|
||||
|
||||
@override
|
||||
String get homeEmptyTitle => 'Home is empty';
|
||||
String get homeEmptyTitle => 'No search providers yet';
|
||||
|
||||
@override
|
||||
String get homeEmptySubtitle =>
|
||||
'Install your first extension to unlock search and browsing.';
|
||||
String get homeEmptySubtitle => 'Install an extension to continue.';
|
||||
|
||||
@override
|
||||
String get homeSupports => 'Supports: Track, Album, Playlist, Artist URLs';
|
||||
|
||||
@@ -30,11 +30,10 @@ class AppLocalizationsPt extends AppLocalizations {
|
||||
String get homeSubtitle => 'Paste a Spotify link or search by name';
|
||||
|
||||
@override
|
||||
String get homeEmptyTitle => 'Home is empty';
|
||||
String get homeEmptyTitle => 'No search providers yet';
|
||||
|
||||
@override
|
||||
String get homeEmptySubtitle =>
|
||||
'Install your first extension to unlock search and browsing.';
|
||||
String get homeEmptySubtitle => 'Install an extension to continue.';
|
||||
|
||||
@override
|
||||
String get homeSupports => 'Supports: Track, Album, Playlist, Artist URLs';
|
||||
|
||||
@@ -30,11 +30,10 @@ class AppLocalizationsRu extends AppLocalizations {
|
||||
String get homeSubtitle => 'Вставьте ссылку Spotify или ищите по названию';
|
||||
|
||||
@override
|
||||
String get homeEmptyTitle => 'Home is empty';
|
||||
String get homeEmptyTitle => 'No search providers yet';
|
||||
|
||||
@override
|
||||
String get homeEmptySubtitle =>
|
||||
'Install your first extension to unlock search and browsing.';
|
||||
String get homeEmptySubtitle => 'Install an extension to continue.';
|
||||
|
||||
@override
|
||||
String get homeSupports =>
|
||||
|
||||
@@ -31,11 +31,10 @@ class AppLocalizationsTr extends AppLocalizations {
|
||||
'Bir Spotify bağlantısı yapıştırın veya şarkı arayın';
|
||||
|
||||
@override
|
||||
String get homeEmptyTitle => 'Home is empty';
|
||||
String get homeEmptyTitle => 'No search providers yet';
|
||||
|
||||
@override
|
||||
String get homeEmptySubtitle =>
|
||||
'Install your first extension to unlock search and browsing.';
|
||||
String get homeEmptySubtitle => 'Install an extension to continue.';
|
||||
|
||||
@override
|
||||
String get homeSupports =>
|
||||
|
||||
@@ -30,11 +30,10 @@ class AppLocalizationsZh extends AppLocalizations {
|
||||
String get homeSubtitle => 'Paste a Spotify link or search by name';
|
||||
|
||||
@override
|
||||
String get homeEmptyTitle => 'Home is empty';
|
||||
String get homeEmptyTitle => 'No search providers yet';
|
||||
|
||||
@override
|
||||
String get homeEmptySubtitle =>
|
||||
'Install your first extension to unlock search and browsing.';
|
||||
String get homeEmptySubtitle => 'Install an extension to continue.';
|
||||
|
||||
@override
|
||||
String get homeSupports => 'Supports: Track, Album, Playlist, Artist URLs';
|
||||
|
||||
@@ -29,11 +29,11 @@
|
||||
"@homeSubtitle": {
|
||||
"description": "Subtitle shown below search box"
|
||||
},
|
||||
"homeEmptyTitle": "Home is empty",
|
||||
"homeEmptyTitle": "No search providers yet",
|
||||
"@homeEmptyTitle": {
|
||||
"description": "Title shown on home when no providers are available yet"
|
||||
},
|
||||
"homeEmptySubtitle": "Install your first extension to unlock search and browsing.",
|
||||
"homeEmptySubtitle": "Install an extension to continue.",
|
||||
"@homeEmptySubtitle": {
|
||||
"description": "Subtitle shown on home when no providers are available yet"
|
||||
},
|
||||
|
||||
@@ -29,6 +29,14 @@
|
||||
"@homeSubtitle": {
|
||||
"description": "Subtitle shown below search box"
|
||||
},
|
||||
"homeEmptyTitle": "Belum ada provider pencarian",
|
||||
"@homeEmptyTitle": {
|
||||
"description": "Title shown on home when no providers are available yet"
|
||||
},
|
||||
"homeEmptySubtitle": "Pasang ekstensi untuk melanjutkan.",
|
||||
"@homeEmptySubtitle": {
|
||||
"description": "Subtitle shown on home when no providers are available yet"
|
||||
},
|
||||
"homeSupports": "Mendukung: URL Track, Album, Playlist, Artis",
|
||||
"@homeSupports": {
|
||||
"description": "Info text about supported URL types"
|
||||
|
||||
+179
-57
@@ -509,6 +509,27 @@ class _HomeTabState extends ConsumerState<HomeTab>
|
||||
defaultBuiltInSearchProviderId;
|
||||
}
|
||||
|
||||
bool _hasSearchProvider(
|
||||
String? explicitSearchProvider,
|
||||
List<Extension> extensions,
|
||||
List<BuiltInProviderSpec> builtInProviders,
|
||||
) {
|
||||
final explicit = explicitSearchProvider?.trim();
|
||||
if (explicit != null && explicit.isNotEmpty) {
|
||||
if (builtInProviders.any((p) => p.supportsSearch && p.id == explicit)) {
|
||||
return true;
|
||||
}
|
||||
if (extensions.any(
|
||||
(ext) => ext.enabled && ext.hasCustomSearch && ext.id == explicit,
|
||||
)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return extensions.any((ext) => ext.enabled && ext.hasCustomSearch) ||
|
||||
builtInProviders.any((provider) => provider.supportsSearch);
|
||||
}
|
||||
|
||||
String? _sanitizeSearchFilterForProvider(
|
||||
String? filter,
|
||||
String? currentSearchProvider,
|
||||
@@ -707,6 +728,8 @@ class _HomeTabState extends ConsumerState<HomeTab>
|
||||
bool _isLiveSearchEnabled() {
|
||||
final settings = ref.read(settingsProvider);
|
||||
final extState = ref.read(extensionProvider);
|
||||
if (!extState.isInitialized && extState.error == null) return true;
|
||||
|
||||
final searchProvider = _resolveSearchProvider(
|
||||
settings.searchProvider,
|
||||
extState.extensions,
|
||||
@@ -774,8 +797,13 @@ class _HomeTabState extends ConsumerState<HomeTab>
|
||||
}
|
||||
|
||||
Future<void> _performSearch(String query, {String? filterOverride}) async {
|
||||
var extState = ref.read(extensionProvider);
|
||||
if (!extState.isInitialized && extState.error == null) {
|
||||
await ref.read(extensionProvider.notifier).waitForInitialization();
|
||||
extState = ref.read(extensionProvider);
|
||||
}
|
||||
|
||||
final settings = ref.read(settingsProvider);
|
||||
final extState = ref.read(extensionProvider);
|
||||
final searchProvider = _resolveSearchProvider(
|
||||
settings.searchProvider,
|
||||
extState.extensions,
|
||||
@@ -1015,9 +1043,7 @@ class _HomeTabState extends ConsumerState<HomeTab>
|
||||
);
|
||||
return;
|
||||
}
|
||||
ref
|
||||
.read(downloadQueueProvider.notifier)
|
||||
.addToQueue(track, service);
|
||||
ref.read(downloadQueueProvider.notifier).addToQueue(track, service);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(context.l10n.snackbarAddedToQueue(track.name)),
|
||||
@@ -1299,6 +1325,15 @@ class _HomeTabState extends ConsumerState<HomeTab>
|
||||
settingsProvider.select((s) => s.defaultSearchTab),
|
||||
);
|
||||
final extensions = ref.watch(extensionProvider.select((s) => s.extensions));
|
||||
final extensionReadiness = ref.watch(
|
||||
extensionProvider.select(
|
||||
(s) => (
|
||||
isInitialized: s.isInitialized,
|
||||
error: s.error,
|
||||
builtInProviders: s.builtInProviders,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final hasExploreContent = ref.watch(
|
||||
exploreProvider.select((s) => s.sections.isNotEmpty),
|
||||
@@ -1336,9 +1371,14 @@ class _HomeTabState extends ConsumerState<HomeTab>
|
||||
recentModeRequested &&
|
||||
(!hasSearchInput || hasShortSearchInput || !hasActualResults) &&
|
||||
!isLoading;
|
||||
final hasSearchProvider =
|
||||
(_resolveSearchProvider(explicitSearchProvider, extensions) ?? '')
|
||||
.isNotEmpty;
|
||||
final isSearchProviderLoading =
|
||||
!extensionReadiness.isInitialized && extensionReadiness.error == null;
|
||||
final hasSearchProvider = _hasSearchProvider(
|
||||
explicitSearchProvider,
|
||||
extensions,
|
||||
extensionReadiness.builtInProviders,
|
||||
);
|
||||
final showSearchBar = hasSearchProvider || isSearchProviderLoading;
|
||||
final hasResults =
|
||||
hasSearchInput || hasActualResults || isLoading || showRecentAccess;
|
||||
final showExplore =
|
||||
@@ -1349,6 +1389,7 @@ class _HomeTabState extends ConsumerState<HomeTab>
|
||||
(hasHomeFeedExtension || hasExploreContent) &&
|
||||
hasExploreContent;
|
||||
final showEmptyHomeState =
|
||||
!isSearchProviderLoading &&
|
||||
!hasSearchProvider &&
|
||||
!hasHomeFeedExtension &&
|
||||
!hasExploreContent &&
|
||||
@@ -1442,56 +1483,15 @@ class _HomeTabState extends ConsumerState<HomeTab>
|
||||
curve: Curves.easeOut,
|
||||
child: (hasResults || showExplore)
|
||||
? const SizedBox.shrink()
|
||||
: Column(
|
||||
children: [
|
||||
SizedBox(height: screenHeight * 0.06),
|
||||
Container(
|
||||
width: 96,
|
||||
height: 96,
|
||||
decoration: BoxDecoration(
|
||||
color: colorScheme.primary,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Image.asset(
|
||||
'assets/images/logo-transparant.png',
|
||||
color: colorScheme.onPrimary,
|
||||
fit: BoxFit.contain,
|
||||
errorBuilder: (_, _, _) => ClipRRect(
|
||||
borderRadius: BorderRadius.circular(24),
|
||||
child: Image.asset(
|
||||
'assets/images/logo.png',
|
||||
width: 96,
|
||||
height: 96,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
showEmptyHomeState
|
||||
? context.l10n.homeEmptyTitle
|
||||
: 'SpotiFLAC Mobile',
|
||||
style: Theme.of(context).textTheme.headlineSmall
|
||||
?.copyWith(fontWeight: FontWeight.bold),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
showEmptyHomeState
|
||||
? context.l10n.homeEmptySubtitle
|
||||
: context.l10n.homeSubtitle,
|
||||
textAlign: TextAlign.center,
|
||||
style: Theme.of(context).textTheme.bodyMedium
|
||||
?.copyWith(
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
],
|
||||
: _buildHomeIntro(
|
||||
colorScheme: colorScheme,
|
||||
screenHeight: screenHeight,
|
||||
showEmptyHomeState: showEmptyHomeState,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
if (hasSearchProvider)
|
||||
if (showSearchBar)
|
||||
SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.fromLTRB(
|
||||
@@ -1690,6 +1690,98 @@ class _HomeTabState extends ConsumerState<HomeTab>
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildHomeIntro({
|
||||
required ColorScheme colorScheme,
|
||||
required double screenHeight,
|
||||
required bool showEmptyHomeState,
|
||||
}) {
|
||||
if (showEmptyHomeState) {
|
||||
final emptyHeight = (screenHeight - 220).clamp(280.0, 520.0).toDouble();
|
||||
return SizedBox(
|
||||
height: emptyHeight,
|
||||
child: Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.extension_outlined,
|
||||
size: 56,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
context.l10n.homeEmptyTitle,
|
||||
textAlign: TextAlign.center,
|
||||
style: Theme.of(context).textTheme.titleMedium?.copyWith(
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
context.l10n.homeEmptySubtitle,
|
||||
textAlign: TextAlign.center,
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24),
|
||||
child: Column(
|
||||
children: [
|
||||
SizedBox(height: screenHeight * 0.06),
|
||||
Container(
|
||||
width: 96,
|
||||
height: 96,
|
||||
decoration: BoxDecoration(
|
||||
color: colorScheme.primary,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Image.asset(
|
||||
'assets/images/logo-transparant.png',
|
||||
color: colorScheme.onPrimary,
|
||||
fit: BoxFit.contain,
|
||||
errorBuilder: (_, _, _) => ClipRRect(
|
||||
borderRadius: BorderRadius.circular(24),
|
||||
child: Image.asset(
|
||||
'assets/images/logo.png',
|
||||
width: 96,
|
||||
height: 96,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'SpotiFLAC Mobile',
|
||||
textAlign: TextAlign.center,
|
||||
style: Theme.of(
|
||||
context,
|
||||
).textTheme.headlineSmall?.copyWith(fontWeight: FontWeight.bold),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
context.l10n.homeSubtitle,
|
||||
textAlign: TextAlign.center,
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _onEmbeddedCoverChanged() {
|
||||
if (!mounted || _embeddedCoverRefreshScheduled) return;
|
||||
_embeddedCoverRefreshScheduled = true;
|
||||
@@ -2176,9 +2268,7 @@ class _HomeTabState extends ConsumerState<HomeTab>
|
||||
);
|
||||
return;
|
||||
}
|
||||
ref
|
||||
.read(downloadQueueProvider.notifier)
|
||||
.addToQueue(track, service);
|
||||
ref.read(downloadQueueProvider.notifier).addToQueue(track, service);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(context.l10n.snackbarAddedToQueue(track.name))),
|
||||
);
|
||||
@@ -3636,13 +3726,45 @@ class _SearchProviderDropdown extends ConsumerWidget {
|
||||
final rawCurrentProvider = ref.watch(
|
||||
settingsProvider.select((s) => s.searchProvider),
|
||||
);
|
||||
final extensions = ref.watch(extensionProvider.select((s) => s.extensions));
|
||||
final extensionState = ref.watch(extensionProvider);
|
||||
final extensions = extensionState.extensions;
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
final searchProviders = extensions
|
||||
.where((ext) => ext.enabled && ext.hasCustomSearch)
|
||||
.toList();
|
||||
final builtInProviders = builtInSearchProviderSpecs;
|
||||
final hasAnyProvider =
|
||||
searchProviders.isNotEmpty || builtInProviders.isNotEmpty;
|
||||
final isProviderLoading =
|
||||
!extensionState.isInitialized && extensionState.error == null;
|
||||
|
||||
if (!hasAnyProvider) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 12, right: 8),
|
||||
child: SizedBox(
|
||||
width: 28,
|
||||
height: 28,
|
||||
child: Center(
|
||||
child: isProviderLoading
|
||||
? SizedBox(
|
||||
width: 18,
|
||||
height: 18,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
color: colorScheme.primary,
|
||||
),
|
||||
)
|
||||
: Icon(
|
||||
Icons.search_off,
|
||||
size: 20,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
final resolvedCurrentProvider =
|
||||
rawCurrentProvider != null &&
|
||||
rawCurrentProvider.isNotEmpty &&
|
||||
|
||||
Reference in New Issue
Block a user