diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index 8e09a30..165c002 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -30,7 +30,7 @@ android { applicationId = "com.example.the_wallpaper_company" // You can update the following values to match your application needs. // For more information, see: https://flutter.dev/to/review-gradle-config. - minSdk = 23 + minSdk = flutter.minSdkVersion targetSdk = flutter.targetSdkVersion versionCode = flutter.versionCode versionName = flutter.versionName @@ -60,4 +60,4 @@ dependencies { coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.5") // Other dependencies for your project... -} \ No newline at end of file +} diff --git a/l10n.yaml b/l10n.yaml new file mode 100644 index 0000000..1437ccc --- /dev/null +++ b/l10n.yaml @@ -0,0 +1,4 @@ +arb-dir: lib/l10n +template-arb-file: app_en.arb +output-localization-file: app_localizations.dart +output-class: AppLocalizations diff --git a/lib/core/localizations/app_localizations.dart b/lib/core/localizations/app_localizations.dart new file mode 100644 index 0000000..81a8231 --- /dev/null +++ b/lib/core/localizations/app_localizations.dart @@ -0,0 +1,252 @@ +import 'package:flutter/material.dart'; +import '../providers/language_provider.dart'; + +class AppLocalizations { + final Locale locale; + + AppLocalizations(this.locale); + + static AppLocalizations? of(BuildContext context) { + return Localizations.of(context, AppLocalizations); + } + + static const LocalizationsDelegate delegate = + _AppLocalizationsDelegate(); + + // App strings + String get appTitle { + switch (locale.languageCode) { + case 'hi': + return 'द वॉलपेपर कंपनी'; + default: + return 'The Wallpaper Co.'; + } + } + + String get searchHint { + switch (locale.languageCode) { + case 'hi': + return 'शीर्षक या श्रेणी द्वारा वॉलपेपर खोजें...'; + default: + return 'Search wallpapers by title or category...'; + } + } + + String get home { + switch (locale.languageCode) { + case 'hi': + return 'होम'; + default: + return 'Home'; + } + } + + String get favorites { + switch (locale.languageCode) { + case 'hi': + return 'पसंदीदा'; + default: + return 'Favorites'; + } + } + + String get all { + switch (locale.languageCode) { + case 'hi': + return 'सभी'; + default: + return 'All'; + } + } + + String get nature { + switch (locale.languageCode) { + case 'hi': + return 'प्रकृति'; + default: + return 'Nature'; + } + } + + String get abstract { + switch (locale.languageCode) { + case 'hi': + return 'अमूर्त'; + default: + return 'Abstract'; + } + } + + String get urban { + switch (locale.languageCode) { + case 'hi': + return 'शहरी'; + default: + return 'Urban'; + } + } + + String get minimal { + switch (locale.languageCode) { + case 'hi': + return 'न्यूनतम'; + default: + return 'Minimal'; + } + } + + String get space { + switch (locale.languageCode) { + case 'hi': + return 'अंतरिक्ष'; + default: + return 'Space'; + } + } + + String get animals { + switch (locale.languageCode) { + case 'hi': + return 'जानवर'; + default: + return 'Animals'; + } + } + + String get art { + switch (locale.languageCode) { + case 'hi': + return 'कला'; + default: + return 'Art'; + } + } + + String get cars { + switch (locale.languageCode) { + case 'hi': + return 'कारें'; + default: + return 'Cars'; + } + } + + String get noResultsTitle { + switch (locale.languageCode) { + case 'hi': + return 'कोई वॉलपेपर नहीं मिला'; + default: + return 'No wallpapers found'; + } + } + + String noResultsMessage(String query) { + switch (locale.languageCode) { + case 'hi': + return '"$query" के लिए कोई परिणाम नहीं'; + default: + return 'No results for "$query"'; + } + } + + String get clearSearch { + switch (locale.languageCode) { + case 'hi': + return 'खोज साफ़ करें'; + default: + return 'Clear Search'; + } + } + + String get download { + switch (locale.languageCode) { + case 'hi': + return 'डाउनलोड'; + default: + return 'Download'; + } + } + + String get setWallpaper { + switch (locale.languageCode) { + case 'hi': + return 'वॉलपेपर सेट करें'; + default: + return 'Set Wallpaper'; + } + } + + String get share { + switch (locale.languageCode) { + case 'hi': + return 'साझा करें'; + default: + return 'Share'; + } + } + + String get loading { + switch (locale.languageCode) { + case 'hi': + return 'लोड हो रहा है...'; + default: + return 'Loading...'; + } + } + + String get error { + switch (locale.languageCode) { + case 'hi': + return 'त्रुटि'; + default: + return 'Error'; + } + } + + String get retry { + switch (locale.languageCode) { + case 'hi': + return 'पुनः प्रयास करें'; + default: + return 'Retry'; + } + } + + String get noFavorites { + switch (locale.languageCode) { + case 'hi': + return 'अभी तक कोई पसंदीदा नहीं'; + default: + return 'No favorites yet'; + } + } + + String get noFavoritesMessage { + switch (locale.languageCode) { + case 'hi': + return 'यहाँ देखने के लिए कुछ वॉलपेपर को अपने पसंदीदा में जोड़ें'; + default: + return 'Add some wallpapers to your favorites to see them here'; + } + } +} + +class _AppLocalizationsDelegate + extends LocalizationsDelegate { + const _AppLocalizationsDelegate(); + + @override + bool isSupported(Locale locale) { + return LanguageProvider.supportedLocales + .map((l) => l.languageCode) + .contains(locale.languageCode); + } + + @override + Future load(Locale locale) async { + return AppLocalizations(locale); + } + + @override + bool shouldReload(_AppLocalizationsDelegate old) => false; +} diff --git a/lib/core/localizations/wallpaper_localizer.dart b/lib/core/localizations/wallpaper_localizer.dart new file mode 100644 index 0000000..a84bf27 --- /dev/null +++ b/lib/core/localizations/wallpaper_localizer.dart @@ -0,0 +1,140 @@ +import 'package:flutter/material.dart'; +import 'app_localizations.dart'; + +class WallpaperLocalizer { + static String getLocalizedWallpaperName( + BuildContext context, + String originalTitle, + ) { + final localizations = AppLocalizations.of(context)!; + + // Convert title to lowercase for comparison + final lowerTitle = originalTitle.toLowerCase().trim(); + + // Map common wallpaper titles to localized names + final Map titleMapping = { + // Nature wallpapers + 'mountain': _getLocalizedText(context, 'Mountain', 'पहाड़'), + 'forest': _getLocalizedText(context, 'Forest', 'जंगल'), + 'ocean': _getLocalizedText(context, 'Ocean', 'समुद्र'), + 'sunset': _getLocalizedText(context, 'Sunset', 'सूर्यास्त'), + 'sunrise': _getLocalizedText(context, 'Sunrise', 'सूर्योदय'), + 'lake': _getLocalizedText(context, 'Lake', 'झील'), + 'river': _getLocalizedText(context, 'River', 'नदी'), + 'beach': _getLocalizedText(context, 'Beach', 'समुद्र तट'), + 'desert': _getLocalizedText(context, 'Desert', 'रेगिस्तान'), + 'waterfall': _getLocalizedText(context, 'Waterfall', 'झरना'), + + // Abstract wallpapers + 'abstract': _getLocalizedText(context, 'Abstract', 'अमूर्त'), + 'pattern': _getLocalizedText(context, 'Pattern', 'पैटर्न'), + 'geometric': _getLocalizedText(context, 'Geometric', 'ज्यामितीय'), + 'colorful': _getLocalizedText(context, 'Colorful', 'रंगबिरंगा'), + 'gradient': _getLocalizedText(context, 'Gradient', 'ग्रेडिएंट'), + + // Space wallpapers + 'galaxy': _getLocalizedText(context, 'Galaxy', 'आकाशगंगा'), + 'nebula': _getLocalizedText(context, 'Nebula', 'नेब्यूला'), + 'planet': _getLocalizedText(context, 'Planet', 'ग्रह'), + 'stars': _getLocalizedText(context, 'Stars', 'तारे'), + 'moon': _getLocalizedText(context, 'Moon', 'चाँद'), + 'earth': _getLocalizedText(context, 'Earth', 'पृथ्वी'), + 'space': _getLocalizedText(context, 'Space', 'अंतरिक्ष'), + + // Urban wallpapers + 'city': _getLocalizedText(context, 'City', 'शहर'), + 'skyline': _getLocalizedText(context, 'Skyline', 'आकाश रेखा'), + 'building': _getLocalizedText(context, 'Building', 'इमारत'), + 'street': _getLocalizedText(context, 'Street', 'सड़क'), + 'architecture': _getLocalizedText(context, 'Architecture', 'वास्तुकला'), + 'bridge': _getLocalizedText(context, 'Bridge', 'पुल'), + 'night city': _getLocalizedText(context, 'Night City', 'रात का शहर'), + + // Animals + 'cat': _getLocalizedText(context, 'Cat', 'बिल्ली'), + 'dog': _getLocalizedText(context, 'Dog', 'कुत्ता'), + 'tiger': _getLocalizedText(context, 'Tiger', 'बाघ'), + 'lion': _getLocalizedText(context, 'Lion', 'शेर'), + 'elephant': _getLocalizedText(context, 'Elephant', 'हाथी'), + 'bird': _getLocalizedText(context, 'Bird', 'पक्षी'), + 'eagle': _getLocalizedText(context, 'Eagle', 'चील'), + 'wolf': _getLocalizedText(context, 'Wolf', 'भेड़िया'), + + // Minimal + 'minimal': _getLocalizedText(context, 'Minimal', 'न्यूनतम'), + 'simple': _getLocalizedText(context, 'Simple', 'सरल'), + 'clean': _getLocalizedText(context, 'Clean', 'साफ'), + 'white': _getLocalizedText(context, 'White', 'सफेद'), + 'black': _getLocalizedText(context, 'Black', 'काला'), + + // Art + 'painting': _getLocalizedText(context, 'Painting', 'चित्र'), + 'artwork': _getLocalizedText(context, 'Artwork', 'कलाकृति'), + 'drawing': _getLocalizedText(context, 'Drawing', 'चित्रकला'), + 'sketch': _getLocalizedText(context, 'Sketch', 'स्केच'), + + // Cars + 'car': _getLocalizedText(context, 'Car', 'कार'), + 'sports car': _getLocalizedText(context, 'Sports Car', 'स्पोर्ट्स कार'), + 'luxury car': _getLocalizedText(context, 'Luxury Car', 'लक्जरी कार'), + 'vintage car': _getLocalizedText(context, 'Vintage Car', 'विंटेज कार'), + 'motorcycle': _getLocalizedText(context, 'Motorcycle', 'मोटरसाइकिल'), + }; + + // Try exact match first + if (titleMapping.containsKey(lowerTitle)) { + return titleMapping[lowerTitle]!; + } + + // Try partial matches for compound titles + for (final entry in titleMapping.entries) { + if (lowerTitle.contains(entry.key) || entry.key.contains(lowerTitle)) { + return entry.value; + } + } + + // If no match found, return original title + return originalTitle; + } + + static String _getLocalizedText( + BuildContext context, + String englishText, + String hindiText, + ) { + final locale = Localizations.localeOf(context); + return locale.languageCode == 'hi' ? hindiText : englishText; + } + + // Helper method to get localized category display name + static String getLocalizedCategoryName( + BuildContext context, + String category, + ) { + final localizations = AppLocalizations.of(context)!; + final lowerCategory = category.toLowerCase().trim(); + + switch (lowerCategory) { + case 'nature': + return localizations.nature; + case 'abstract': + return localizations.abstract; + case 'urban': + return localizations.urban; + case 'minimal': + return localizations.minimal; + case 'space': + return localizations.space; + case 'animals': + return localizations.animals; + case 'art': + return localizations.art; + case 'cars': + return localizations.cars; + case 'all': + return localizations.all; + default: + return category; + } + } +} diff --git a/lib/core/providers/language_provider.dart b/lib/core/providers/language_provider.dart new file mode 100644 index 0000000..7e92be5 --- /dev/null +++ b/lib/core/providers/language_provider.dart @@ -0,0 +1,67 @@ +import 'package:flutter/material.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +class LanguageProvider extends ChangeNotifier { + Locale _currentLocale = const Locale('en'); + static const String _languagePreferenceKey = 'selectedLanguage'; + + Locale get currentLocale => _currentLocale; + + LanguageProvider() { + _loadLanguagePreference(); + } + + void setLanguage(Locale locale) { + if (_currentLocale == locale) return; + + _currentLocale = locale; + _saveLanguagePreference(); + notifyListeners(); + } + + void toggleLanguage() { + if (_currentLocale.languageCode == 'en') { + setLanguage(const Locale('hi')); + } else { + setLanguage(const Locale('en')); + } + } + + bool get isHindi => _currentLocale.languageCode == 'hi'; + bool get isEnglish => _currentLocale.languageCode == 'en'; + + String get currentLanguageName { + switch (_currentLocale.languageCode) { + case 'hi': + return 'हिंदी'; + case 'en': + default: + return 'English'; + } + } + + Future _loadLanguagePreference() async { + try { + final prefs = await SharedPreferences.getInstance(); + final languageCode = prefs.getString(_languagePreferenceKey) ?? 'en'; + _currentLocale = Locale(languageCode); + notifyListeners(); + } catch (e) { + debugPrint('Error loading language preference: $e'); + } + } + + Future _saveLanguagePreference() async { + try { + final prefs = await SharedPreferences.getInstance(); + await prefs.setString( + _languagePreferenceKey, + _currentLocale.languageCode, + ); + } catch (e) { + debugPrint('Error saving language preference: $e'); + } + } + + static const List supportedLocales = [Locale('en'), Locale('hi')]; +} diff --git a/lib/core/providers/theme_provider.dart b/lib/core/providers/theme_provider.dart new file mode 100644 index 0000000..30c9bd3 --- /dev/null +++ b/lib/core/providers/theme_provider.dart @@ -0,0 +1,82 @@ +import 'package:flutter/material.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +class ThemeProvider extends ChangeNotifier { + bool _isDarkMode = false; + static const String _themePreferenceKey = 'isDarkMode'; + + bool get isDarkMode => _isDarkMode; + + ThemeProvider() { + _loadThemePreference(); + } + + void toggleTheme() { + _isDarkMode = !_isDarkMode; + _saveThemePreference(); + notifyListeners(); + } + + void setDarkMode(bool value) { + _isDarkMode = value; + _saveThemePreference(); + notifyListeners(); + } + + Future _loadThemePreference() async { + try { + final prefs = await SharedPreferences.getInstance(); + _isDarkMode = prefs.getBool(_themePreferenceKey) ?? false; + notifyListeners(); + } catch (e) { + debugPrint('Error loading theme preference: $e'); + } + } + + Future _saveThemePreference() async { + try { + final prefs = await SharedPreferences.getInstance(); + await prefs.setBool(_themePreferenceKey, _isDarkMode); + } catch (e) { + debugPrint('Error saving theme preference: $e'); + } + } + + ThemeData get lightTheme => ThemeData.light().copyWith( + primaryColor: Colors.pinkAccent, + scaffoldBackgroundColor: const Color(0xFFF5F5F5), + appBarTheme: const AppBarTheme( + backgroundColor: Colors.transparent, + elevation: 0, + iconTheme: IconThemeData(color: Colors.black87), + titleTextStyle: TextStyle( + color: Colors.black87, + fontSize: 20, + fontWeight: FontWeight.w600, + ), + ), + textTheme: const TextTheme( + bodyLarge: TextStyle(color: Colors.black87), + bodyMedium: TextStyle(color: Colors.black87), + ), + ); + + ThemeData get darkTheme => ThemeData.dark().copyWith( + primaryColor: Colors.pinkAccent, + scaffoldBackgroundColor: Colors.black, + appBarTheme: const AppBarTheme( + backgroundColor: Colors.transparent, + elevation: 0, + iconTheme: IconThemeData(color: Colors.white), + titleTextStyle: TextStyle( + color: Colors.white, + fontSize: 20, + fontWeight: FontWeight.w600, + ), + ), + textTheme: const TextTheme( + bodyLarge: TextStyle(color: Colors.white), + bodyMedium: TextStyle(color: Colors.white), + ), + ); +} diff --git a/lib/core/utils/category_utils.dart b/lib/core/utils/category_utils.dart new file mode 100644 index 0000000..ef3d630 --- /dev/null +++ b/lib/core/utils/category_utils.dart @@ -0,0 +1,86 @@ +import 'package:flutter/material.dart'; +import '../localizations/app_localizations.dart'; + +class CategoryUtils { + static String getLocalizedCategoryName( + BuildContext context, + String categoryName, + ) { + final localizations = AppLocalizations.of(context); + + switch (categoryName.toLowerCase()) { + case 'all': + return localizations?.all ?? 'All'; + case 'nature': + return localizations?.nature ?? 'Nature'; + case 'abstract': + return localizations?.abstract ?? 'Abstract'; + case 'urban': + return localizations?.urban ?? 'Urban'; + case 'minimal': + return localizations?.minimal ?? 'Minimal'; + case 'space': + return _getSpaceTranslation(context); + case 'animals': + return _getAnimalsTranslation(context); + case 'art': + return _getArtTranslation(context); + case 'cars': + return _getCarsTranslation(context); + case 'technology': + return _getTechnologyTranslation(context); + default: + return categoryName; + } + } + + static String _getSpaceTranslation(BuildContext context) { + final locale = Localizations.localeOf(context); + switch (locale.languageCode) { + case 'hi': + return 'अंतरिक्ष'; + default: + return 'Space'; + } + } + + static String _getAnimalsTranslation(BuildContext context) { + final locale = Localizations.localeOf(context); + switch (locale.languageCode) { + case 'hi': + return 'जानवर'; + default: + return 'Animals'; + } + } + + static String _getArtTranslation(BuildContext context) { + final locale = Localizations.localeOf(context); + switch (locale.languageCode) { + case 'hi': + return 'कला'; + default: + return 'Art'; + } + } + + static String _getCarsTranslation(BuildContext context) { + final locale = Localizations.localeOf(context); + switch (locale.languageCode) { + case 'hi': + return 'कारें'; + default: + return 'Cars'; + } + } + + static String _getTechnologyTranslation(BuildContext context) { + final locale = Localizations.localeOf(context); + switch (locale.languageCode) { + case 'hi': + return 'तकनीक'; + default: + return 'Technology'; + } + } +} diff --git a/lib/features/home/presentation/screen/home_screen.dart b/lib/features/home/presentation/screen/home_screen.dart index 6c87e6e..0a9e1b9 100644 --- a/lib/features/home/presentation/screen/home_screen.dart +++ b/lib/features/home/presentation/screen/home_screen.dart @@ -12,6 +12,8 @@ import 'package:the_wallpaper_company/features/home/presentation/widgets/wallpap import 'package:the_wallpaper_company/features/home/provider/wallpaper_provider.dart'; import 'package:the_wallpaper_company/firebase_message.dart'; import '../widgets/category_carousel.dart'; +import '../widgets/search_bar_widget.dart'; +import '../widgets/no_results_widget.dart'; import 'package:the_wallpaper_company/core/constants.dart'; import 'package:the_wallpaper_company/features/home/presentation/widgets/wallpaper_tile.dart'; import 'package:pull_to_refresh_flutter3/pull_to_refresh_flutter3.dart'; @@ -121,60 +123,76 @@ class _HomeScreenState extends State { sigmaX: 12, sigmaY: 12, ), - child: CategoryCarousel( - categories: AppConstants.categories, - selectedCategory: - provider.selectedCategory, - onCategorySelected: - provider.selectCategory, + child: Column( + children: [ + const SearchBarWidget(), + CategoryCarousel( + categories: + AppConstants.categories, + selectedCategory: + provider.selectedCategory, + onCategorySelected: + provider.selectCategory, + ), + ], ), ), ), ), // expandedHeight: 72, - toolbarHeight: 150, + toolbarHeight: 220, ), - SliverPadding( - padding: const EdgeInsets.symmetric( - horizontal: 8, - vertical: 8, - ), - sliver: SliverMasonryGrid.count( - crossAxisCount: 2, - mainAxisSpacing: 8, - crossAxisSpacing: 8, - childCount: - visibleWallpapers.length + - (provider.isPaginating ? 1 : 0), - itemBuilder: (context, index) { - if (provider.isPaginating && - index == visibleWallpapers.length) { - return const WallpaperShimmer(); - } - final wallpaper = - visibleWallpapers[index]; - return InkWell( - onTap: () { - Navigator.of(context).push( - MaterialPageRoute( - builder: (context) => - WallpaperScreen( - wallpapers: - visibleWallpapers, - initialIndex: index, + visibleWallpapers.isEmpty && + provider.searchQuery.isNotEmpty + ? SliverFillRemaining( + child: NoResultsWidget( + searchQuery: provider.searchQuery, + onClearSearch: provider.clearSearch, + ), + ) + : SliverPadding( + padding: const EdgeInsets.symmetric( + horizontal: 8, + vertical: 8, + ), + sliver: SliverMasonryGrid.count( + crossAxisCount: 2, + mainAxisSpacing: 8, + crossAxisSpacing: 8, + childCount: + visibleWallpapers.length + + (provider.isPaginating ? 1 : 0), + itemBuilder: (context, index) { + if (provider.isPaginating && + index == + visibleWallpapers + .length) { + return const WallpaperShimmer(); + } + final wallpaper = + visibleWallpapers[index]; + return InkWell( + onTap: () { + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => + WallpaperScreen( + wallpapers: + visibleWallpapers, + initialIndex: index, + ), ), - ), - ); - }, - child: WallpaperTile( - imageUrl: wallpaper.imageUrl, - title: wallpaper.title, - id: wallpaper.id, + ); + }, + child: WallpaperTile( + imageUrl: wallpaper.imageUrl, + title: wallpaper.title, + id: wallpaper.id, + ), + ); + }, ), - ); - }, - ), - ), + ), ], ), ), diff --git a/lib/features/home/presentation/screen/wallpaper_detail_screen.dart b/lib/features/home/presentation/screen/wallpaper_detail_screen.dart index ad34af9..e9fc639 100644 --- a/lib/features/home/presentation/screen/wallpaper_detail_screen.dart +++ b/lib/features/home/presentation/screen/wallpaper_detail_screen.dart @@ -6,6 +6,8 @@ import 'package:provider/provider.dart'; import 'package:animate_do/animate_do.dart'; import 'package:the_wallpaper_company/features/favorite/provider/favorite_provider.dart'; import 'package:the_wallpaper_company/features/home/provider/wallpaper_provider.dart'; +import '../../../../core/localizations/app_localizations.dart'; +import '../../../../core/localizations/wallpaper_localizer.dart'; import '../../models/wallpaper_model.dart'; class WallpaperDetailScreen extends StatefulWidget { @@ -110,7 +112,10 @@ class _WallpaperDetailScreenState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - widget.wallpaper.title, + WallpaperLocalizer.getLocalizedWallpaperName( + context, + widget.wallpaper.title, + ), style: const TextStyle( color: Colors.white, fontWeight: FontWeight.bold, @@ -127,7 +132,10 @@ class _WallpaperDetailScreenState extends State { Container( padding: const EdgeInsets.symmetric(vertical: 6), child: Text( - widget.wallpaper.category, + WallpaperLocalizer.getLocalizedCategoryName( + context, + widget.wallpaper.category, + ), style: const TextStyle( color: Colors.white70, fontSize: 14, @@ -154,9 +162,9 @@ class _WallpaperDetailScreenState extends State { ), onPressed: () {}, icon: const Icon(Icons.download, size: 22), - label: const Text( - 'Download', - style: TextStyle( + label: Text( + AppLocalizations.of(context)!.download, + style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 16, ), diff --git a/lib/features/home/presentation/screen/wallpaper_screen.dart b/lib/features/home/presentation/screen/wallpaper_screen.dart index 6e47561..9eb8798 100644 --- a/lib/features/home/presentation/screen/wallpaper_screen.dart +++ b/lib/features/home/presentation/screen/wallpaper_screen.dart @@ -5,6 +5,8 @@ import 'package:flutter_cache_manager/flutter_cache_manager.dart'; import 'package:shimmer/shimmer.dart'; import 'package:provider/provider.dart'; import 'package:the_wallpaper_company/features/favorite/provider/favorite_provider.dart'; +import 'package:the_wallpaper_company/core/localizations/app_localizations.dart'; +import 'package:the_wallpaper_company/core/localizations/wallpaper_localizer.dart'; import '../../models/wallpaper_model.dart'; class WallpaperScreen extends StatefulWidget { @@ -130,7 +132,10 @@ class _WallpaperScreenState extends State crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - wallpaper.title, + WallpaperLocalizer.getLocalizedWallpaperName( + context, + wallpaper.title, + ), style: const TextStyle( color: Colors.white, fontWeight: FontWeight.bold, @@ -149,7 +154,10 @@ class _WallpaperScreenState extends State vertical: 6, ), child: Text( - wallpaper.category, + WallpaperLocalizer.getLocalizedCategoryName( + context, + wallpaper.category, + ), style: const TextStyle( color: Colors.white70, fontSize: 14, @@ -176,9 +184,9 @@ class _WallpaperScreenState extends State ), onPressed: () {}, icon: const Icon(Icons.download, size: 22), - label: const Text( - 'Download', - style: TextStyle( + label: Text( + AppLocalizations.of(context)!.download, + style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 16, ), diff --git a/lib/features/home/presentation/widgets/category_carousel.dart b/lib/features/home/presentation/widgets/category_carousel.dart index 5bc38e4..5685f85 100644 --- a/lib/features/home/presentation/widgets/category_carousel.dart +++ b/lib/features/home/presentation/widgets/category_carousel.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:carousel_slider/carousel_slider.dart'; +import '../../../../core/utils/category_utils.dart'; class CategoryCarousel extends StatelessWidget { final List> categories; @@ -77,7 +78,10 @@ class CategoryCarousel extends StatelessWidget { left: 0, right: 0, child: Text( - category['name']!, + CategoryUtils.getLocalizedCategoryName( + context, + category['name']!, + ), textAlign: TextAlign.center, style: TextStyle( color: isSelected ? Colors.white : Colors.white70, diff --git a/lib/features/home/presentation/widgets/language_toggle_button.dart b/lib/features/home/presentation/widgets/language_toggle_button.dart new file mode 100644 index 0000000..42fe39e --- /dev/null +++ b/lib/features/home/presentation/widgets/language_toggle_button.dart @@ -0,0 +1,70 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:the_wallpaper_company/core/providers/language_provider.dart'; + +class LanguageToggleButton extends StatelessWidget { + const LanguageToggleButton({super.key}); + + @override + Widget build(BuildContext context) { + return Consumer( + builder: (context, languageProvider, child) { + return Container( + margin: const EdgeInsets.only(right: 8), + decoration: BoxDecoration( + color: Theme.of(context).brightness == Brightness.dark + ? Colors.grey[800]?.withValues(alpha: 0.8) + : Colors.white.withValues(alpha: 0.8), + borderRadius: BorderRadius.circular(25), + boxShadow: [ + BoxShadow( + color: Colors.black.withValues(alpha: 0.1), + blurRadius: 10, + offset: const Offset(0, 2), + ), + ], + ), + child: Material( + color: Colors.transparent, + child: InkWell( + borderRadius: BorderRadius.circular(25), + onTap: () { + languageProvider.toggleLanguage(); + }, + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 12, + vertical: 8, + ), + child: AnimatedSwitcher( + duration: const Duration(milliseconds: 300), + transitionBuilder: + (Widget child, Animation animation) { + return ScaleTransition( + scale: animation, + child: FadeTransition( + opacity: animation, + child: child, + ), + ); + }, + child: Text( + languageProvider.isHindi ? 'EN' : 'हि', + key: ValueKey(languageProvider.currentLocale.languageCode), + style: TextStyle( + color: languageProvider.isHindi + ? Colors.orange[600] + : Colors.blue[600], + fontSize: 14, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ), + ), + ); + }, + ); + } +} diff --git a/lib/features/home/presentation/widgets/no_results_widget.dart b/lib/features/home/presentation/widgets/no_results_widget.dart new file mode 100644 index 0000000..61a4fc9 --- /dev/null +++ b/lib/features/home/presentation/widgets/no_results_widget.dart @@ -0,0 +1,75 @@ +import 'package:flutter/material.dart'; +import '../../../../core/localizations/app_localizations.dart'; + +class NoResultsWidget extends StatelessWidget { + final String searchQuery; + final VoidCallback onClearSearch; + + const NoResultsWidget({ + super.key, + required this.searchQuery, + required this.onClearSearch, + }); + + @override + Widget build(BuildContext context) { + return Center( + child: Padding( + padding: const EdgeInsets.all(32.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.search_off, + size: 64, + color: Theme.of(context).brightness == Brightness.dark + ? Colors.grey[600] + : Colors.grey[400], + ), + const SizedBox(height: 16), + Text( + AppLocalizations.of(context)?.noResultsTitle ?? + 'No wallpapers found', + style: Theme.of(context).textTheme.headlineSmall?.copyWith( + color: Theme.of(context).brightness == Brightness.dark + ? Colors.grey[400] + : Colors.grey[600], + fontWeight: FontWeight.w600, + ), + ), + const SizedBox(height: 8), + Text( + AppLocalizations.of( + context, + )?.noResultsMessage(searchQuery.trim()) ?? + 'No results for "${searchQuery.trim()}"', + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + color: Theme.of(context).brightness == Brightness.dark + ? Colors.grey[500] + : Colors.grey[500], + ), + textAlign: TextAlign.center, + ), + const SizedBox(height: 24), + ElevatedButton.icon( + onPressed: onClearSearch, + icon: const Icon(Icons.clear), + label: Text( + AppLocalizations.of(context)?.clearSearch ?? 'Clear Search', + ), + style: ElevatedButton.styleFrom( + padding: const EdgeInsets.symmetric( + horizontal: 24, + vertical: 12, + ), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(25), + ), + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/features/home/presentation/widgets/search_bar_widget.dart b/lib/features/home/presentation/widgets/search_bar_widget.dart new file mode 100644 index 0000000..df895c3 --- /dev/null +++ b/lib/features/home/presentation/widgets/search_bar_widget.dart @@ -0,0 +1,126 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import '../../provider/wallpaper_provider.dart'; +import 'theme_toggle_button.dart'; +import 'language_toggle_button.dart'; +import '../../../../core/localizations/app_localizations.dart'; + +class SearchBarWidget extends StatefulWidget { + const SearchBarWidget({super.key}); + + @override + State createState() => _SearchBarWidgetState(); +} + +class _SearchBarWidgetState extends State { + final TextEditingController _searchController = TextEditingController(); + final FocusNode _focusNode = FocusNode(); + + @override + void initState() { + super.initState(); + final provider = Provider.of(context, listen: false); + _searchController.text = provider.searchQuery; + } + + @override + void dispose() { + _searchController.dispose(); + _focusNode.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Consumer( + builder: (context, provider, child) { + return Container( + margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + child: Row( + children: [ + Expanded( + child: Container( + decoration: BoxDecoration( + color: Theme.of(context).brightness == Brightness.dark + ? Colors.grey[800]?.withValues(alpha: 0.8) + : Colors.white.withValues(alpha: 0.8), + borderRadius: BorderRadius.circular(25), + boxShadow: [ + BoxShadow( + color: Colors.black.withValues(alpha: 0.1), + blurRadius: 10, + offset: const Offset(0, 2), + ), + ], + ), + child: TextField( + controller: _searchController, + focusNode: _focusNode, + onChanged: (value) { + provider.updateSearchQuery(value); + }, + decoration: InputDecoration( + hintText: + AppLocalizations.of(context)?.searchHint ?? + 'Search wallpapers by title or category...', + hintStyle: TextStyle( + color: Theme.of(context).brightness == Brightness.dark + ? Colors.grey[400] + : Colors.grey[600], + fontSize: 16, + ), + prefixIcon: Icon( + provider.searchQuery.isNotEmpty + ? Icons.search + : Icons.search, + color: provider.searchQuery.isNotEmpty + ? Colors.pinkAccent + : (Theme.of(context).brightness == Brightness.dark + ? Colors.grey[400] + : Colors.grey[600]), + size: 24, + ), + suffixIcon: provider.searchQuery.isNotEmpty + ? IconButton( + icon: Icon( + Icons.clear, + color: + Theme.of(context).brightness == + Brightness.dark + ? Colors.grey[400] + : Colors.grey[600], + size: 20, + ), + onPressed: () { + _searchController.clear(); + provider.clearSearch(); + _focusNode.unfocus(); + }, + ) + : null, + border: InputBorder.none, + contentPadding: const EdgeInsets.symmetric( + horizontal: 20, + vertical: 16, + ), + ), + style: TextStyle( + color: Theme.of(context).brightness == Brightness.dark + ? Colors.white + : Colors.black87, + fontSize: 16, + ), + ), + ), + ), + const SizedBox(width: 8), + const LanguageToggleButton(), + const SizedBox(width: 8), + const ThemeToggleButton(), + ], + ), + ); + }, + ); + } +} diff --git a/lib/features/home/presentation/widgets/theme_toggle_button.dart b/lib/features/home/presentation/widgets/theme_toggle_button.dart new file mode 100644 index 0000000..80845bf --- /dev/null +++ b/lib/features/home/presentation/widgets/theme_toggle_button.dart @@ -0,0 +1,66 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:the_wallpaper_company/core/providers/theme_provider.dart'; + +class ThemeToggleButton extends StatelessWidget { + const ThemeToggleButton({super.key}); + + @override + Widget build(BuildContext context) { + return Consumer( + builder: (context, themeProvider, child) { + return Container( + margin: const EdgeInsets.only(right: 8), + decoration: BoxDecoration( + color: Theme.of(context).brightness == Brightness.dark + ? Colors.grey[800]?.withValues(alpha: 0.8) + : Colors.white.withValues(alpha: 0.8), + borderRadius: BorderRadius.circular(25), + boxShadow: [ + BoxShadow( + color: Colors.black.withValues(alpha: 0.1), + blurRadius: 10, + offset: const Offset(0, 2), + ), + ], + ), + child: Material( + color: Colors.transparent, + child: InkWell( + borderRadius: BorderRadius.circular(25), + onTap: () { + themeProvider.toggleTheme(); + }, + child: Padding( + padding: const EdgeInsets.all(12), + child: AnimatedSwitcher( + duration: const Duration(milliseconds: 300), + transitionBuilder: + (Widget child, Animation animation) { + return RotationTransition( + turns: animation, + child: FadeTransition( + opacity: animation, + child: child, + ), + ); + }, + child: Icon( + themeProvider.isDarkMode + ? Icons.light_mode_rounded + : Icons.dark_mode_rounded, + key: ValueKey(themeProvider.isDarkMode), + color: themeProvider.isDarkMode + ? Colors.yellow[300] + : Colors.indigo[700], + size: 24, + ), + ), + ), + ), + ), + ); + }, + ); + } +} diff --git a/lib/features/home/provider/wallpaper_provider.dart b/lib/features/home/provider/wallpaper_provider.dart index 67cdd90..01bd521 100644 --- a/lib/features/home/provider/wallpaper_provider.dart +++ b/lib/features/home/provider/wallpaper_provider.dart @@ -8,19 +8,38 @@ class WallpaperProvider extends ChangeNotifier { List _wallpapers = []; int _visibleCount = 10; bool _isPaginating = false; + String _searchQuery = ''; - List get wallpapers => _selectedCategory == 'All' - ? _wallpapers.take(_visibleCount).toList() - : _wallpapers - .where((w) => w.category == _selectedCategory) - .toList() - .take(_visibleCount) - .toList(); + List get wallpapers { + List filteredWallpapers = _wallpapers; + + // Apply search filter + if (_searchQuery.isNotEmpty) { + filteredWallpapers = _wallpapers.where((wallpaper) { + return wallpaper.title.toLowerCase().contains( + _searchQuery.toLowerCase(), + ) || + wallpaper.category.toLowerCase().contains( + _searchQuery.toLowerCase(), + ); + }).toList(); + } + + // Apply category filter + if (_selectedCategory != 'All') { + filteredWallpapers = filteredWallpapers + .where((w) => w.category == _selectedCategory) + .toList(); + } + + return filteredWallpapers.take(_visibleCount).toList(); + } void addWallpaper(Wallpaper wallpaper) { _wallpapers.add(wallpaper); notifyListeners(); } + bool _isLoading = false; bool get isLoading => _isLoading; bool get isPaginating => _isPaginating; @@ -29,6 +48,8 @@ class WallpaperProvider extends ChangeNotifier { String _selectedCategory = 'All'; String get selectedCategory => _selectedCategory; + String get searchQuery => _searchQuery; + Future fetchWallpapers() async { _isLoading = true; notifyListeners(); @@ -54,12 +75,49 @@ class WallpaperProvider extends ChangeNotifier { notifyListeners(); } + void updateSearchQuery(String query) { + _searchQuery = query; + _visibleCount = 10; // Reset visible count when searching + notifyListeners(); + } + + void clearSearch() { + _searchQuery = ''; + _visibleCount = 10; + notifyListeners(); + } + void loadMore() { - if (_visibleCount < _wallpapers.length && !_isPaginating) { + // Get the total count of filtered wallpapers + List filteredWallpapers = _wallpapers; + + // Apply search filter + if (_searchQuery.isNotEmpty) { + filteredWallpapers = _wallpapers.where((wallpaper) { + return wallpaper.title.toLowerCase().contains( + _searchQuery.toLowerCase(), + ) || + wallpaper.category.toLowerCase().contains( + _searchQuery.toLowerCase(), + ); + }).toList(); + } + + // Apply category filter + if (_selectedCategory != 'All') { + filteredWallpapers = filteredWallpapers + .where((w) => w.category == _selectedCategory) + .toList(); + } + + if (_visibleCount < filteredWallpapers.length && !_isPaginating) { _isPaginating = true; notifyListeners(); Future.delayed(const Duration(milliseconds: 500), () { - _visibleCount = (_visibleCount + 10).clamp(0, _wallpapers.length); + _visibleCount = (_visibleCount + 10).clamp( + 0, + filteredWallpapers.length, + ); _isPaginating = false; notifyListeners(); }); diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb new file mode 100644 index 0000000..befba3d --- /dev/null +++ b/lib/l10n/app_en.arb @@ -0,0 +1,205 @@ +{ + "@@locale": "en", + "appTitle": "The Wallpaper Co.", + "@appTitle": { + "description": "The title of the application" + }, + "searchHint": "Search wallpapers by title or category...", + "@searchHint": { + "description": "Hint text for search bar" + }, + "home": "Home", + "@home": { + "description": "Home tab label" + }, + "favorites": "Favorites", + "@favorites": { + "description": "Favorites tab label" + }, + "categories": "Categories", + "@categories": { + "description": "Categories section label" + }, + "all": "All", + "@all": { + "description": "All categories option" + }, + "nature": "Nature", + "@nature": { + "description": "Nature category" + }, + "abstract": "Abstract", + "@abstract": { + "description": "Abstract category" + }, + "urban": "Urban", + "@urban": { + "description": "Urban category" + }, + "minimal": "Minimal", + "@minimal": { + "description": "Minimal category" + }, + "space": "Space", + "@space": { + "description": "Space category" + }, + "animals": "Animals", + "@animals": { + "description": "Animals category" + }, + "technology": "Technology", + "@technology": { + "description": "Technology category" + }, + "art": "Art", + "@art": { + "description": "Art category" + }, + "noResultsTitle": "No wallpapers found", + "@noResultsTitle": { + "description": "Title when no search results are found" + }, + "noResultsMessage": "No results for \"{query}\"", + "@noResultsMessage": { + "description": "Message when no search results are found", + "placeholders": { + "query": { + "type": "String", + "description": "The search query that returned no results" + } + } + }, + "clearSearch": "Clear Search", + "@clearSearch": { + "description": "Button to clear search" + }, + "download": "Download", + "@download": { + "description": "Download button text" + }, + "setWallpaper": "Set Wallpaper", + "@setWallpaper": { + "description": "Set wallpaper button text" + }, + "share": "Share", + "@share": { + "description": "Share button text" + }, + "addToFavorites": "Add to Favorites", + "@addToFavorites": { + "description": "Add to favorites button text" + }, + "removeFromFavorites": "Remove from Favorites", + "@removeFromFavorites": { + "description": "Remove from favorites button text" + }, + "downloadSuccess": "Downloaded successfully", + "@downloadSuccess": { + "description": "Success message when wallpaper is downloaded" + }, + "downloadFailed": "Download failed", + "@downloadFailed": { + "description": "Error message when download fails" + }, + "wallpaperSet": "Wallpaper set successfully", + "@wallpaperSet": { + "description": "Success message when wallpaper is set" + }, + "wallpaperSetFailed": "Failed to set wallpaper", + "@wallpaperSetFailed": { + "description": "Error message when setting wallpaper fails" + }, + "loading": "Loading...", + "@loading": { + "description": "Loading text" + }, + "retry": "Retry", + "@retry": { + "description": "Retry button text" + }, + "error": "Error", + "@error": { + "description": "Error title" + }, + "networkError": "Network error occurred", + "@networkError": { + "description": "Network error message" + }, + "language": "Language", + "@language": { + "description": "Language selection label" + }, + "english": "English", + "@english": { + "description": "English language option" + }, + "hindi": "हिंदी", + "@hindi": { + "description": "Hindi language option" + }, + "settings": "Settings", + "@settings": { + "description": "Settings title" + }, + "about": "About", + "@about": { + "description": "About section" + }, + "version": "Version", + "@version": { + "description": "App version label" + }, + "developer": "Developer", + "@developer": { + "description": "Developer label" + }, + "pullToRefresh": "Pull to refresh", + "@pullToRefresh": { + "description": "Pull to refresh instruction" + }, + "releaseToRefresh": "Release to refresh", + "@releaseToRefresh": { + "description": "Release to refresh instruction" + }, + "refreshing": "Refreshing...", + "@refreshing": { + "description": "Refreshing status text" + }, + "noFavorites": "No favorites yet", + "@noFavorites": { + "description": "Message when no favorites are saved" + }, + "noFavoritesMessage": "Add some wallpapers to your favorites to see them here", + "@noFavoritesMessage": { + "description": "Detailed message when no favorites are saved" + }, + "mountain": "Mountain", + "@mountain": { + "description": "Mountain wallpaper name" + }, + "forest": "Forest", + "@forest": { + "description": "Forest wallpaper name" + }, + "ocean": "Ocean", + "@ocean": { + "description": "Ocean wallpaper name" + }, + "sunset": "Sunset", + "@sunset": { + "description": "Sunset wallpaper name" + }, + "sunrise": "Sunrise", + "@sunrise": { + "description": "Sunrise wallpaper name" + }, + "galaxy": "Galaxy", + "@galaxy": { + "description": "Galaxy wallpaper name" + }, + "city": "City", + "@city": { + "description": "City wallpaper name" + } +} \ No newline at end of file diff --git a/lib/l10n/app_hi.arb b/lib/l10n/app_hi.arb new file mode 100644 index 0000000..614e39c --- /dev/null +++ b/lib/l10n/app_hi.arb @@ -0,0 +1,52 @@ +{ + "@@locale": "hi", + "appTitle": "द वॉलपेपर कंपनी", + "searchHint": "शीर्षक या श्रेणी द्वारा वॉलपेपर खोजें...", + "home": "होम", + "favorites": "पसंदीदा", + "categories": "श्रेणियां", + "all": "सभी", + "nature": "प्रकृति", + "abstract": "अमूर्त", + "urban": "शहरी", + "minimal": "न्यूनतम", + "space": "अंतरिक्ष", + "animals": "जानवर", + "technology": "तकनीक", + "art": "कला", + "noResultsTitle": "कोई वॉलपेपर नहीं मिला", + "noResultsMessage": "\"{query}\" के लिए कोई परिणाम नहीं", + "clearSearch": "खोज साफ़ करें", + "download": "डाउनलोड", + "setWallpaper": "वॉलपेपर सेट करें", + "share": "साझा करें", + "addToFavorites": "पसंदीदा में जोड़ें", + "removeFromFavorites": "पसंदीदा से हटाएं", + "downloadSuccess": "सफलतापूर्वक डाउनलोड हुआ", + "downloadFailed": "डाउनलोड असफल", + "wallpaperSet": "वॉलपेपर सफलतापूर्वक सेट हुआ", + "wallpaperSetFailed": "वॉलपेपर सेट करना असफल", + "loading": "लोड हो रहा है...", + "retry": "पुनः प्रयास करें", + "error": "त्रुटि", + "networkError": "नेटवर्क त्रुटि हुई", + "language": "भाषा", + "english": "English", + "hindi": "हिंदी", + "settings": "सेटिंग्स", + "about": "के बारे में", + "version": "संस्करण", + "developer": "डेवलपर", + "pullToRefresh": "रिफ्रेश करने के लिए खींचें", + "releaseToRefresh": "रिफ्रेश करने के लिए छोड़ें", + "refreshing": "रिफ्रेश हो रहा है...", + "noFavorites": "अभी तक कोई पसंदीदा नहीं", + "noFavoritesMessage": "यहाँ देखने के लिए कुछ वॉलपेपर को अपने पसंदीदा में जोड़ें", + "mountain": "पहाड़", + "forest": "जंगल", + "ocean": "समुद्र", + "sunset": "सूर्यास्त", + "sunrise": "सूर्योदय", + "galaxy": "आकाशगंगा", + "city": "शहर" +} \ No newline at end of file diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart new file mode 100644 index 0000000..d40c038 --- /dev/null +++ b/lib/l10n/app_localizations.dart @@ -0,0 +1,428 @@ +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:intl/intl.dart' as intl; + +import 'app_localizations_en.dart'; +import 'app_localizations_hi.dart'; + +// ignore_for_file: type=lint + +/// Callers can lookup localized strings with an instance of AppLocalizations +/// returned by `AppLocalizations.of(context)`. +/// +/// Applications need to include `AppLocalizations.delegate()` in their app's +/// `localizationDelegates` list, and the locales they support in the app's +/// `supportedLocales` list. For example: +/// +/// ```dart +/// import 'l10n/app_localizations.dart'; +/// +/// return MaterialApp( +/// localizationsDelegates: AppLocalizations.localizationsDelegates, +/// supportedLocales: AppLocalizations.supportedLocales, +/// home: MyApplicationHome(), +/// ); +/// ``` +/// +/// ## Update pubspec.yaml +/// +/// Please make sure to update your pubspec.yaml to include the following +/// packages: +/// +/// ```yaml +/// dependencies: +/// # Internationalization support. +/// flutter_localizations: +/// sdk: flutter +/// intl: any # Use the pinned version from flutter_localizations +/// +/// # Rest of dependencies +/// ``` +/// +/// ## iOS Applications +/// +/// iOS applications define key application metadata, including supported +/// locales, in an Info.plist file that is built into the application bundle. +/// To configure the locales supported by your app, you’ll need to edit this +/// file. +/// +/// First, open your project’s ios/Runner.xcworkspace Xcode workspace file. +/// Then, in the Project Navigator, open the Info.plist file under the Runner +/// project’s Runner folder. +/// +/// Next, select the Information Property List item, select Add Item from the +/// Editor menu, then select Localizations from the pop-up menu. +/// +/// Select and expand the newly-created Localizations item then, for each +/// locale your application supports, add a new item and select the locale +/// you wish to add from the pop-up menu in the Value field. This list should +/// be consistent with the languages listed in the AppLocalizations.supportedLocales +/// property. +abstract class AppLocalizations { + AppLocalizations(String locale) + : localeName = intl.Intl.canonicalizedLocale(locale.toString()); + + final String localeName; + + static AppLocalizations? of(BuildContext context) { + return Localizations.of(context, AppLocalizations); + } + + static const LocalizationsDelegate delegate = + _AppLocalizationsDelegate(); + + /// A list of this localizations delegate along with the default localizations + /// delegates. + /// + /// Returns a list of localizations delegates containing this delegate along with + /// GlobalMaterialLocalizations.delegate, GlobalCupertinoLocalizations.delegate, + /// and GlobalWidgetsLocalizations.delegate. + /// + /// Additional delegates can be added by appending to this list in + /// MaterialApp. This list does not have to be used at all if a custom list + /// of delegates is preferred or required. + static const List> localizationsDelegates = + >[ + delegate, + GlobalMaterialLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + ]; + + /// A list of this localizations delegate's supported locales. + static const List supportedLocales = [ + Locale('en'), + Locale('hi'), + ]; + + /// The title of the application + /// + /// In en, this message translates to: + /// **'The Wallpaper Co.'** + String get appTitle; + + /// Hint text for search bar + /// + /// In en, this message translates to: + /// **'Search wallpapers by title or category...'** + String get searchHint; + + /// Home tab label + /// + /// In en, this message translates to: + /// **'Home'** + String get home; + + /// Favorites tab label + /// + /// In en, this message translates to: + /// **'Favorites'** + String get favorites; + + /// Categories section label + /// + /// In en, this message translates to: + /// **'Categories'** + String get categories; + + /// All categories option + /// + /// In en, this message translates to: + /// **'All'** + String get all; + + /// Nature category + /// + /// In en, this message translates to: + /// **'Nature'** + String get nature; + + /// Abstract category + /// + /// In en, this message translates to: + /// **'Abstract'** + String get abstract; + + /// Urban category + /// + /// In en, this message translates to: + /// **'Urban'** + String get urban; + + /// Minimal category + /// + /// In en, this message translates to: + /// **'Minimal'** + String get minimal; + + /// Space category + /// + /// In en, this message translates to: + /// **'Space'** + String get space; + + /// Animals category + /// + /// In en, this message translates to: + /// **'Animals'** + String get animals; + + /// Technology category + /// + /// In en, this message translates to: + /// **'Technology'** + String get technology; + + /// Art category + /// + /// In en, this message translates to: + /// **'Art'** + String get art; + + /// Title when no search results are found + /// + /// In en, this message translates to: + /// **'No wallpapers found'** + String get noResultsTitle; + + /// Message when no search results are found + /// + /// In en, this message translates to: + /// **'No results for \"{query}\"'** + String noResultsMessage(String query); + + /// Button to clear search + /// + /// In en, this message translates to: + /// **'Clear Search'** + String get clearSearch; + + /// Download button text + /// + /// In en, this message translates to: + /// **'Download'** + String get download; + + /// Set wallpaper button text + /// + /// In en, this message translates to: + /// **'Set Wallpaper'** + String get setWallpaper; + + /// Share button text + /// + /// In en, this message translates to: + /// **'Share'** + String get share; + + /// Add to favorites button text + /// + /// In en, this message translates to: + /// **'Add to Favorites'** + String get addToFavorites; + + /// Remove from favorites button text + /// + /// In en, this message translates to: + /// **'Remove from Favorites'** + String get removeFromFavorites; + + /// Success message when wallpaper is downloaded + /// + /// In en, this message translates to: + /// **'Downloaded successfully'** + String get downloadSuccess; + + /// Error message when download fails + /// + /// In en, this message translates to: + /// **'Download failed'** + String get downloadFailed; + + /// Success message when wallpaper is set + /// + /// In en, this message translates to: + /// **'Wallpaper set successfully'** + String get wallpaperSet; + + /// Error message when setting wallpaper fails + /// + /// In en, this message translates to: + /// **'Failed to set wallpaper'** + String get wallpaperSetFailed; + + /// Loading text + /// + /// In en, this message translates to: + /// **'Loading...'** + String get loading; + + /// Retry button text + /// + /// In en, this message translates to: + /// **'Retry'** + String get retry; + + /// Error title + /// + /// In en, this message translates to: + /// **'Error'** + String get error; + + /// Network error message + /// + /// In en, this message translates to: + /// **'Network error occurred'** + String get networkError; + + /// Language selection label + /// + /// In en, this message translates to: + /// **'Language'** + String get language; + + /// English language option + /// + /// In en, this message translates to: + /// **'English'** + String get english; + + /// Hindi language option + /// + /// In en, this message translates to: + /// **'हिंदी'** + String get hindi; + + /// Settings title + /// + /// In en, this message translates to: + /// **'Settings'** + String get settings; + + /// About section + /// + /// In en, this message translates to: + /// **'About'** + String get about; + + /// App version label + /// + /// In en, this message translates to: + /// **'Version'** + String get version; + + /// Developer label + /// + /// In en, this message translates to: + /// **'Developer'** + String get developer; + + /// Pull to refresh instruction + /// + /// In en, this message translates to: + /// **'Pull to refresh'** + String get pullToRefresh; + + /// Release to refresh instruction + /// + /// In en, this message translates to: + /// **'Release to refresh'** + String get releaseToRefresh; + + /// Refreshing status text + /// + /// In en, this message translates to: + /// **'Refreshing...'** + String get refreshing; + + /// Message when no favorites are saved + /// + /// In en, this message translates to: + /// **'No favorites yet'** + String get noFavorites; + + /// Detailed message when no favorites are saved + /// + /// In en, this message translates to: + /// **'Add some wallpapers to your favorites to see them here'** + String get noFavoritesMessage; + + /// Mountain wallpaper name + /// + /// In en, this message translates to: + /// **'Mountain'** + String get mountain; + + /// Forest wallpaper name + /// + /// In en, this message translates to: + /// **'Forest'** + String get forest; + + /// Ocean wallpaper name + /// + /// In en, this message translates to: + /// **'Ocean'** + String get ocean; + + /// Sunset wallpaper name + /// + /// In en, this message translates to: + /// **'Sunset'** + String get sunset; + + /// Sunrise wallpaper name + /// + /// In en, this message translates to: + /// **'Sunrise'** + String get sunrise; + + /// Galaxy wallpaper name + /// + /// In en, this message translates to: + /// **'Galaxy'** + String get galaxy; + + /// City wallpaper name + /// + /// In en, this message translates to: + /// **'City'** + String get city; +} + +class _AppLocalizationsDelegate + extends LocalizationsDelegate { + const _AppLocalizationsDelegate(); + + @override + Future load(Locale locale) { + return SynchronousFuture(lookupAppLocalizations(locale)); + } + + @override + bool isSupported(Locale locale) => + ['en', 'hi'].contains(locale.languageCode); + + @override + bool shouldReload(_AppLocalizationsDelegate old) => false; +} + +AppLocalizations lookupAppLocalizations(Locale locale) { + // Lookup logic when only language code is specified. + switch (locale.languageCode) { + case 'en': + return AppLocalizationsEn(); + case 'hi': + return AppLocalizationsHi(); + } + + throw FlutterError( + 'AppLocalizations.delegate failed to load unsupported locale "$locale". This is likely ' + 'an issue with the localizations generation tool. Please file an issue ' + 'on GitHub with a reproducible sample app and the gen-l10n configuration ' + 'that was used.', + ); +} diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart new file mode 100644 index 0000000..3ec4abf --- /dev/null +++ b/lib/l10n/app_localizations_en.dart @@ -0,0 +1,160 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'app_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for English (`en`). +class AppLocalizationsEn extends AppLocalizations { + AppLocalizationsEn([String locale = 'en']) : super(locale); + + @override + String get appTitle => 'The Wallpaper Co.'; + + @override + String get searchHint => 'Search wallpapers by title or category...'; + + @override + String get home => 'Home'; + + @override + String get favorites => 'Favorites'; + + @override + String get categories => 'Categories'; + + @override + String get all => 'All'; + + @override + String get nature => 'Nature'; + + @override + String get abstract => 'Abstract'; + + @override + String get urban => 'Urban'; + + @override + String get minimal => 'Minimal'; + + @override + String get space => 'Space'; + + @override + String get animals => 'Animals'; + + @override + String get technology => 'Technology'; + + @override + String get art => 'Art'; + + @override + String get noResultsTitle => 'No wallpapers found'; + + @override + String noResultsMessage(String query) { + return 'No results for \"$query\"'; + } + + @override + String get clearSearch => 'Clear Search'; + + @override + String get download => 'Download'; + + @override + String get setWallpaper => 'Set Wallpaper'; + + @override + String get share => 'Share'; + + @override + String get addToFavorites => 'Add to Favorites'; + + @override + String get removeFromFavorites => 'Remove from Favorites'; + + @override + String get downloadSuccess => 'Downloaded successfully'; + + @override + String get downloadFailed => 'Download failed'; + + @override + String get wallpaperSet => 'Wallpaper set successfully'; + + @override + String get wallpaperSetFailed => 'Failed to set wallpaper'; + + @override + String get loading => 'Loading...'; + + @override + String get retry => 'Retry'; + + @override + String get error => 'Error'; + + @override + String get networkError => 'Network error occurred'; + + @override + String get language => 'Language'; + + @override + String get english => 'English'; + + @override + String get hindi => 'हिंदी'; + + @override + String get settings => 'Settings'; + + @override + String get about => 'About'; + + @override + String get version => 'Version'; + + @override + String get developer => 'Developer'; + + @override + String get pullToRefresh => 'Pull to refresh'; + + @override + String get releaseToRefresh => 'Release to refresh'; + + @override + String get refreshing => 'Refreshing...'; + + @override + String get noFavorites => 'No favorites yet'; + + @override + String get noFavoritesMessage => + 'Add some wallpapers to your favorites to see them here'; + + @override + String get mountain => 'Mountain'; + + @override + String get forest => 'Forest'; + + @override + String get ocean => 'Ocean'; + + @override + String get sunset => 'Sunset'; + + @override + String get sunrise => 'Sunrise'; + + @override + String get galaxy => 'Galaxy'; + + @override + String get city => 'City'; +} diff --git a/lib/l10n/app_localizations_hi.dart b/lib/l10n/app_localizations_hi.dart new file mode 100644 index 0000000..62e16a0 --- /dev/null +++ b/lib/l10n/app_localizations_hi.dart @@ -0,0 +1,160 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'app_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Hindi (`hi`). +class AppLocalizationsHi extends AppLocalizations { + AppLocalizationsHi([String locale = 'hi']) : super(locale); + + @override + String get appTitle => 'द वॉलपेपर कंपनी'; + + @override + String get searchHint => 'शीर्षक या श्रेणी द्वारा वॉलपेपर खोजें...'; + + @override + String get home => 'होम'; + + @override + String get favorites => 'पसंदीदा'; + + @override + String get categories => 'श्रेणियां'; + + @override + String get all => 'सभी'; + + @override + String get nature => 'प्रकृति'; + + @override + String get abstract => 'अमूर्त'; + + @override + String get urban => 'शहरी'; + + @override + String get minimal => 'न्यूनतम'; + + @override + String get space => 'अंतरिक्ष'; + + @override + String get animals => 'जानवर'; + + @override + String get technology => 'तकनीक'; + + @override + String get art => 'कला'; + + @override + String get noResultsTitle => 'कोई वॉलपेपर नहीं मिला'; + + @override + String noResultsMessage(String query) { + return '\"$query\" के लिए कोई परिणाम नहीं'; + } + + @override + String get clearSearch => 'खोज साफ़ करें'; + + @override + String get download => 'डाउनलोड'; + + @override + String get setWallpaper => 'वॉलपेपर सेट करें'; + + @override + String get share => 'साझा करें'; + + @override + String get addToFavorites => 'पसंदीदा में जोड़ें'; + + @override + String get removeFromFavorites => 'पसंदीदा से हटाएं'; + + @override + String get downloadSuccess => 'सफलतापूर्वक डाउनलोड हुआ'; + + @override + String get downloadFailed => 'डाउनलोड असफल'; + + @override + String get wallpaperSet => 'वॉलपेपर सफलतापूर्वक सेट हुआ'; + + @override + String get wallpaperSetFailed => 'वॉलपेपर सेट करना असफल'; + + @override + String get loading => 'लोड हो रहा है...'; + + @override + String get retry => 'पुनः प्रयास करें'; + + @override + String get error => 'त्रुटि'; + + @override + String get networkError => 'नेटवर्क त्रुटि हुई'; + + @override + String get language => 'भाषा'; + + @override + String get english => 'English'; + + @override + String get hindi => 'हिंदी'; + + @override + String get settings => 'सेटिंग्स'; + + @override + String get about => 'के बारे में'; + + @override + String get version => 'संस्करण'; + + @override + String get developer => 'डेवलपर'; + + @override + String get pullToRefresh => 'रिफ्रेश करने के लिए खींचें'; + + @override + String get releaseToRefresh => 'रिफ्रेश करने के लिए छोड़ें'; + + @override + String get refreshing => 'रिफ्रेश हो रहा है...'; + + @override + String get noFavorites => 'अभी तक कोई पसंदीदा नहीं'; + + @override + String get noFavoritesMessage => + 'यहाँ देखने के लिए कुछ वॉलपेपर को अपने पसंदीदा में जोड़ें'; + + @override + String get mountain => 'पहाड़'; + + @override + String get forest => 'जंगल'; + + @override + String get ocean => 'समुद्र'; + + @override + String get sunset => 'सूर्यास्त'; + + @override + String get sunrise => 'सूर्योदय'; + + @override + String get galaxy => 'आकाशगंगा'; + + @override + String get city => 'शहर'; +} diff --git a/lib/main.dart b/lib/main.dart index 7749272..6515aea 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -2,9 +2,12 @@ import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:firebase_remote_config/firebase_remote_config.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:provider/provider.dart'; import 'package:the_wallpaper_company/core/constants.dart'; +import 'package:the_wallpaper_company/core/providers/theme_provider.dart'; +import 'package:the_wallpaper_company/core/providers/language_provider.dart'; +import 'package:the_wallpaper_company/core/localizations/app_localizations.dart'; import 'package:the_wallpaper_company/features/favorite/provider/favorite_provider.dart'; import 'package:the_wallpaper_company/features/home/models/wallpaper_model.dart'; import 'package:the_wallpaper_company/features/home/presentation/screen/home_screen.dart'; @@ -27,6 +30,8 @@ Future main() async { runApp( MultiProvider( providers: [ + ChangeNotifierProvider(create: (_) => ThemeProvider()), + ChangeNotifierProvider(create: (_) => LanguageProvider()), ChangeNotifierProvider(create: (_) => WallpaperProvider()), ChangeNotifierProvider(create: (_) => FavoriteProvider()..init()), ], @@ -84,22 +89,28 @@ class _MyAppState extends State { @override Widget build(BuildContext context) { - return MaterialApp( - navigatorKey: navigatorKey, - debugShowCheckedModeBanner: false, - title: 'The Wallpaper Co.', - theme: _isDarkMode - ? ThemeData.dark().copyWith(scaffoldBackgroundColor: Colors.black) - : ThemeData.light().copyWith( - appBarTheme: const AppBarTheme( - systemOverlayStyle: SystemUiOverlayStyle( - statusBarColor: Color(0xFFF5F5F5), - statusBarIconBrightness: Brightness.dark, // dark icons - statusBarBrightness: Brightness.light, // for iOS - ), - ), - ), - home: const HomeScreen(), + return Consumer2( + builder: (context, themeProvider, languageProvider, child) { + return MaterialApp( + navigatorKey: navigatorKey, + debugShowCheckedModeBanner: false, + title: 'The Wallpaper Co.', + theme: themeProvider.lightTheme, + darkTheme: themeProvider.darkTheme, + themeMode: themeProvider.isDarkMode + ? ThemeMode.dark + : ThemeMode.light, + locale: languageProvider.currentLocale, + localizationsDelegates: const [ + AppLocalizations.delegate, + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + ], + supportedLocales: LanguageProvider.supportedLocales, + home: const HomeScreen(), + ); + }, ); } @@ -113,14 +124,41 @@ class _MyAppState extends State { ), ); await remoteConfig.fetchAndActivate(); + final remoteThemeValue = remoteConfig.getBool( + AppConstants.remoteConfigKey, + ); + + // Sync with theme provider if context is available + if (mounted && context.mounted) { + final themeProvider = Provider.of( + context, + listen: false, + ); + themeProvider.setDarkMode(remoteThemeValue); + } + setState(() { - _isDarkMode = remoteConfig.getBool(AppConstants.remoteConfigKey); + _isDarkMode = remoteThemeValue; debugPrint('hello Dark Mode:[$_isDarkMode'); }); + remoteConfig.onConfigUpdated.listen((event) async { await remoteConfig.activate(); + final updatedThemeValue = remoteConfig.getBool( + AppConstants.remoteConfigKey, + ); + + // Sync with theme provider if context is available + if (mounted && context.mounted) { + final themeProvider = Provider.of( + context, + listen: false, + ); + themeProvider.setDarkMode(updatedThemeValue); + } + setState(() { - _isDarkMode = remoteConfig.getBool(AppConstants.remoteConfigKey); + _isDarkMode = updatedThemeValue; }); }); } catch (e, stack) { diff --git a/pubspec.lock b/pubspec.lock index a361883..8931f95 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -326,6 +326,11 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.2" + flutter_localizations: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" flutter_staggered_grid_view: dependency: "direct main" description: @@ -376,30 +381,38 @@ packages: url: "https://pub.dev" source: hosted version: "3.2.0" + intl: + dependency: "direct main" + description: + name: intl + sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" + url: "https://pub.dev" + source: hosted + version: "0.20.2" leak_tracker: dependency: transitive description: name: leak_tracker - sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" + sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de" url: "https://pub.dev" source: hosted - version: "10.0.9" + version: "11.0.2" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 + sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" url: "https://pub.dev" source: hosted - version: "3.0.9" + version: "3.0.10" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.0.2" lints: dependency: transitive description: @@ -745,10 +758,10 @@ packages: dependency: transitive description: name: test_api - sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd + sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00" url: "https://pub.dev" source: hosted - version: "0.7.4" + version: "0.7.6" timezone: dependency: transitive description: @@ -785,10 +798,10 @@ packages: dependency: transitive description: name: vector_math - sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.2.0" vm_service: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index d62f0c5..894a8fe 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: the_wallpaper_company description: "A new Flutter project." # The following line prevents the package from being accidentally published to # pub.dev using `flutter pub publish`. This is preferred for private packages. -publish_to: 'none' # Remove this line if you wish to publish to pub.dev +publish_to: "none" # Remove this line if you wish to publish to pub.dev # The following defines the version and build number for your application. # A version number is three numbers separated by dots, like 1.2.43 @@ -30,6 +30,9 @@ environment: dependencies: flutter: sdk: flutter + flutter_localizations: + sdk: flutter + intl: ^0.20.2 # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. @@ -72,12 +75,14 @@ dev_dependencies: # The following section is specific to Flutter packages. flutter: - # The following line ensures that the Material Icons font is # included with your application, so that you can use the icons in # the material Icons class. uses-material-design: true + # Enable localization + generate: true + # To add assets to your application, add an assets section, like this: # assets: # - images/a_dot_burr.jpeg diff --git a/test/localization_test.dart b/test/localization_test.dart new file mode 100644 index 0000000..f86c327 --- /dev/null +++ b/test/localization_test.dart @@ -0,0 +1,149 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:provider/provider.dart'; +import 'package:the_wallpaper_company/core/providers/language_provider.dart'; +import 'package:the_wallpaper_company/core/localizations/app_localizations.dart'; +import 'package:the_wallpaper_company/features/home/presentation/widgets/language_toggle_button.dart'; + +void main() { + group('Localization Tests', () { + testWidgets('AppLocalizations should provide English strings', ( + WidgetTester tester, + ) async { + final localizations = AppLocalizations(const Locale('en')); + + expect(localizations.appTitle, 'The Wallpaper Co.'); + expect(localizations.home, 'Home'); + expect(localizations.favorites, 'Favorites'); + expect( + localizations.searchHint, + 'Search wallpapers by title or category...', + ); + }); + + testWidgets('AppLocalizations should provide Hindi strings', ( + WidgetTester tester, + ) async { + final localizations = AppLocalizations(const Locale('hi')); + + expect(localizations.appTitle, 'द वॉलपेपर कंपनी'); + expect(localizations.home, 'होम'); + expect(localizations.favorites, 'पसंदीदा'); + expect( + localizations.searchHint, + 'शीर्षक या श्रेणी द्वारा वॉलपेपर खोजें...', + ); + }); + + testWidgets('LanguageProvider should toggle between languages correctly', ( + WidgetTester tester, + ) async { + final languageProvider = LanguageProvider(); + + // Initial state should be English + expect(languageProvider.currentLocale.languageCode, 'en'); + expect(languageProvider.isEnglish, true); + expect(languageProvider.isHindi, false); + + // Toggle to Hindi + languageProvider.toggleLanguage(); + expect(languageProvider.currentLocale.languageCode, 'hi'); + expect(languageProvider.isEnglish, false); + expect(languageProvider.isHindi, true); + + // Toggle back to English + languageProvider.toggleLanguage(); + expect(languageProvider.currentLocale.languageCode, 'en'); + expect(languageProvider.isEnglish, true); + expect(languageProvider.isHindi, false); + }); + + testWidgets( + 'LanguageToggleButton should display correct text for English', + (WidgetTester tester) async { + final languageProvider = LanguageProvider(); + + await tester.pumpWidget( + MaterialApp( + home: ChangeNotifierProvider.value( + value: languageProvider, + child: const Scaffold(body: LanguageToggleButton()), + ), + ), + ); + + // Should show Hindi indicator when in English mode + expect(find.text('हि'), findsOneWidget); + }, + ); + + testWidgets('LanguageToggleButton should display correct text for Hindi', ( + WidgetTester tester, + ) async { + final languageProvider = LanguageProvider(); + languageProvider.setLanguage(const Locale('hi')); // Set to Hindi + + await tester.pumpWidget( + MaterialApp( + home: ChangeNotifierProvider.value( + value: languageProvider, + child: const Scaffold(body: LanguageToggleButton()), + ), + ), + ); + + await tester.pump(); // Allow for animations + + // Should show English indicator when in Hindi mode + expect(find.text('EN'), findsOneWidget); + }); + + testWidgets('LanguageToggleButton tap should toggle language', ( + WidgetTester tester, + ) async { + final languageProvider = LanguageProvider(); + + await tester.pumpWidget( + MaterialApp( + home: ChangeNotifierProvider.value( + value: languageProvider, + child: const Scaffold(body: LanguageToggleButton()), + ), + ), + ); + + // Initial state - English + expect(languageProvider.isEnglish, true); + + // Tap the toggle button + await tester.tap(find.byType(LanguageToggleButton)); + await tester.pump(); + + // Should now be Hindi + expect(languageProvider.isHindi, true); + + // Tap again + await tester.tap(find.byType(LanguageToggleButton)); + await tester.pump(); + + // Should be back to English + expect(languageProvider.isEnglish, true); + }); + + test('noResultsMessage should format query correctly in English', () { + final localizations = AppLocalizations(const Locale('en')); + expect( + localizations.noResultsMessage('nature'), + 'No results for "nature"', + ); + }); + + test('noResultsMessage should format query correctly in Hindi', () { + final localizations = AppLocalizations(const Locale('hi')); + expect( + localizations.noResultsMessage('प्रकृति'), + '"प्रकृति" के लिए कोई परिणाम नहीं', + ); + }); + }); +}