189 lines
6.1 KiB
Dart
189 lines
6.1 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
import 'bloc/bloc.dart';
|
|
import 'bloc/events.dart';
|
|
import 'bloc/state.dart';
|
|
import 'utils/character_service.dart';
|
|
import 'components/locale/l10n/app_locale.dart';
|
|
import 'pages/character_detail_page.dart';
|
|
import 'utils/character_search_delegate.dart';
|
|
|
|
void main() async {
|
|
WidgetsFlutterBinding.ensureInitialized();
|
|
|
|
// Load saved language preference
|
|
final prefs = await SharedPreferences.getInstance();
|
|
final savedLocale = prefs.getString('locale') ?? 'en';
|
|
|
|
runApp(
|
|
MultiBlocProvider(
|
|
providers: [
|
|
BlocProvider<HomeBloc>(
|
|
create: (_) => HomeBloc(CharacterService()),
|
|
),
|
|
BlocProvider<DebouncedSearchCubit>(
|
|
create: (_) => DebouncedSearchCubit(),
|
|
),
|
|
],
|
|
child: MyApp(savedLocale: savedLocale),
|
|
),
|
|
);
|
|
}
|
|
|
|
class MyApp extends StatefulWidget {
|
|
final String savedLocale;
|
|
const MyApp({required this.savedLocale, super.key});
|
|
|
|
@override
|
|
State<MyApp> createState() => _MyAppState();
|
|
}
|
|
|
|
class _MyAppState extends State<MyApp> {
|
|
late Locale _locale;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_locale = Locale(widget.savedLocale);
|
|
}
|
|
|
|
void _changeLanguage(String languageCode) async {
|
|
setState(() {
|
|
_locale = Locale(languageCode);
|
|
});
|
|
|
|
// Save selected language to preferences
|
|
final prefs = await SharedPreferences.getInstance();
|
|
prefs.setString('locale', languageCode);
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return MaterialApp(
|
|
debugShowCheckedModeBanner: false,
|
|
localizationsDelegates: AppLocale.localizationsDelegates,
|
|
supportedLocales: AppLocale.supportedLocales,
|
|
locale: _locale,
|
|
localeResolutionCallback: (locale, supportedLocales) {
|
|
return supportedLocales.contains(locale) ? locale : const Locale('en');
|
|
},
|
|
home: MyHomePage(
|
|
onLanguageChanged: _changeLanguage,
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class MyHomePage extends StatelessWidget {
|
|
final Function(String) onLanguageChanged;
|
|
|
|
const MyHomePage({
|
|
required this.onLanguageChanged,
|
|
super.key,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
context.read<HomeBloc>().add(HomeLoadDataEvent());
|
|
|
|
final appTitle = AppLocale.of(context)?.appTitle ?? 'Default Title';
|
|
|
|
return Scaffold(
|
|
appBar: AppBar(
|
|
title: Text(appTitle),
|
|
actions: [
|
|
IconButton(
|
|
icon: const Icon(Icons.language),
|
|
onPressed: () {
|
|
// Переключение между языками
|
|
final currentLocale = Localizations.localeOf(context).languageCode;
|
|
final newLocale = currentLocale == 'en' ? 'ru' : 'en';
|
|
onLanguageChanged(newLocale);
|
|
},
|
|
),
|
|
IconButton(
|
|
icon: const Icon(Icons.refresh),
|
|
onPressed: () {
|
|
// Отправляем событие для обновления данных
|
|
context.read<HomeBloc>().add(HomeLoadDataEvent());
|
|
},
|
|
),
|
|
IconButton(
|
|
icon: const Icon(Icons.search),
|
|
onPressed: () {
|
|
showSearch(
|
|
context: context,
|
|
delegate: CharacterSearchDelegate(),
|
|
);
|
|
},
|
|
),
|
|
],
|
|
),
|
|
body: BlocBuilder<HomeBloc, HomeState>(
|
|
builder: (context, state) {
|
|
if (state.status == HomeStatus.loading) {
|
|
return const Center(child: CircularProgressIndicator());
|
|
} else if (state.status == HomeStatus.error) {
|
|
return Center(
|
|
child: Text(AppLocale.of(context)?.errorMessage(state.errorMessage) ?? 'Error'));
|
|
} else if (state.status == HomeStatus.loaded) {
|
|
final characters = state.characters;
|
|
|
|
return ListView.builder(
|
|
itemCount: characters.length,
|
|
itemBuilder: (context, index) {
|
|
final characterDTO = characters[index];
|
|
return StatefulBuilder(
|
|
builder: (context, setState) {
|
|
return ListTile(
|
|
leading: SizedBox(
|
|
width: 40,
|
|
child: Image.network(characterDTO.imageUrl, width: 50, height: 50),
|
|
),
|
|
title: Text(characterDTO.name),
|
|
subtitle: Text(characterDTO.typeString),
|
|
trailing: IconButton(
|
|
icon: Icon(
|
|
characterDTO.isLiked ? Icons.favorite : Icons.favorite_border,
|
|
color: characterDTO.isLiked ? Colors.red : Colors.grey,
|
|
),
|
|
onPressed: () {
|
|
setState(() {
|
|
characterDTO.isLiked = !characterDTO.isLiked;
|
|
});
|
|
|
|
final message = characterDTO.isLiked
|
|
? (AppLocale.of(context)?.likeMessage(characterDTO.name) ?? 'Понравился персонаж')
|
|
: (AppLocale.of(context)?.unlikeMessage(characterDTO.name) ?? 'Персонаж больше не понравился');
|
|
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(
|
|
content: Text(message),
|
|
duration: const Duration(seconds: 1),
|
|
),
|
|
);
|
|
},
|
|
),
|
|
onTap: () {
|
|
Navigator.push(
|
|
context,
|
|
MaterialPageRoute(
|
|
builder: (context) => CharacterDetailPage(characterDTO: characterDTO),
|
|
),
|
|
);
|
|
},
|
|
);
|
|
},
|
|
);
|
|
},
|
|
);
|
|
} else {
|
|
return Center(child: Text(AppLocale.of(context)?.noData ?? 'No Data'));
|
|
}
|
|
},
|
|
),
|
|
);
|
|
}
|
|
}
|