264 lines
9.3 KiB
Dart
264 lines
9.3 KiB
Dart
import 'package:card_app/components/extensions/context_x.dart';
|
|
import 'package:card_app/components/utils/debounce.dart';
|
|
import 'package:card_app/domain/models/card.dart';
|
|
import 'package:card_app/presentation/common/svg_objects.dart';
|
|
import 'package:card_app/presentation/home_page/bloc/bloc.dart';
|
|
import 'package:card_app/presentation/home_page/bloc/events.dart';
|
|
import 'package:card_app/presentation/like_bloc/know_bloc.dart';
|
|
import 'package:card_app/presentation/like_bloc/know_event.dart';
|
|
import 'package:card_app/presentation/locale_bloc/locale_events.dart';
|
|
import 'package:card_app/repositories/WordsRepository.dart';
|
|
import 'package:card_app/repositories/mock_repository.dart';
|
|
import 'package:flutter/cupertino.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
|
|
import '../details_page/details_page.dart';
|
|
import '../like_bloc/know_state.dart';
|
|
import '../locale_bloc/locale_bloc.dart';
|
|
import '../locale_bloc/locale_state.dart';
|
|
import 'bloc/state.dart';
|
|
|
|
part 'card.dart';
|
|
|
|
class MyHomePage extends StatefulWidget {
|
|
const MyHomePage({super.key, required this.title});
|
|
|
|
final String title;
|
|
|
|
@override
|
|
State<MyHomePage> createState() => _MyHomePageState();
|
|
}
|
|
|
|
class _MyHomePageState extends State<MyHomePage> {
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
appBar: AppBar(
|
|
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
|
|
title: Text(widget.title),
|
|
),
|
|
body: Body());
|
|
}
|
|
}
|
|
|
|
class Body extends StatefulWidget {
|
|
const Body({super.key});
|
|
|
|
@override
|
|
_BodyState createState() => _BodyState();
|
|
}
|
|
|
|
class _BodyState extends State<Body> {
|
|
final SearchController searchController = SearchController();
|
|
final scrollController = ScrollController();
|
|
// Future<List<CardData>?>? data;
|
|
|
|
@override
|
|
void initState() {
|
|
SvgObjects.init();
|
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
context.read<HomeBloc>().add(const HomeLoadDataEvent());
|
|
context.read<KnowBloc>().add(const LoadKnowsEvent());
|
|
|
|
});
|
|
scrollController.addListener(_onNextPageListener);
|
|
super.initState();
|
|
//data = WordsRepository().loadData(); // Инициализация данных
|
|
}
|
|
|
|
void _onNextPageListener() {
|
|
if (scrollController.offset > scrollController.position.maxScrollExtent - 300) {
|
|
final bloc = context.read<HomeBloc>();
|
|
if (!bloc.state.isPaginationLoading) {
|
|
bloc.add(HomeLoadDataEvent(
|
|
search: searchController.text,
|
|
nextPage: bloc.state.data?.nextPage,
|
|
));
|
|
}
|
|
}
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
searchController.dispose();
|
|
scrollController.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Padding(
|
|
padding: EdgeInsets.only(top: MediaQuery.of(context).padding.top),
|
|
child: Column(children: [
|
|
Row(
|
|
children: [
|
|
Expanded(
|
|
flex: 4,
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(12),
|
|
child: CupertinoSearchTextField(
|
|
placeholder: context.locale.search,
|
|
onChanged: (search) {
|
|
Debounce.run(() => context.read<HomeBloc>().add(HomeLoadDataEvent(search: search)));
|
|
},
|
|
),
|
|
),
|
|
),
|
|
GestureDetector(
|
|
onTap: () =>
|
|
context.read<LocaleBloc>().add(const ChangeLocaleEvent()),
|
|
child: SizedBox.square(
|
|
dimension: 50,
|
|
child: Padding(
|
|
padding: const EdgeInsets.only(right: 12),
|
|
child: BlocBuilder<LocaleBloc, LocaleState>(
|
|
builder: (context, state) {
|
|
return state.currentLocale.languageCode == 'ru'
|
|
? const SvgRu()
|
|
: const SvgUk();
|
|
},
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
BlocBuilder<HomeBloc, HomeState>(
|
|
builder: (context, state) => state.error != null
|
|
? Text(state.error ?? '',
|
|
style: Theme.of(context).textTheme.headlineLarge?.copyWith(color: Colors.red))
|
|
: state.isLoading
|
|
? const CircularProgressIndicator()
|
|
: BlocBuilder<KnowBloc, KnowState>(
|
|
builder: (context, knowState) {
|
|
return Expanded(
|
|
child: RefreshIndicator(
|
|
onRefresh: _onRefresh,
|
|
child: ListView.builder(
|
|
controller: scrollController,
|
|
padding: EdgeInsets.zero,
|
|
itemCount: state.data?.data?.length ?? 0,
|
|
itemBuilder: (context, index) {
|
|
final data = state.data?.data?[index];
|
|
return data != null
|
|
? _Card.fromData(
|
|
data,
|
|
onKnow:_onKnow,
|
|
isKnown: knowState.knownIds?.contains(data.id) == true,
|
|
onTap: () => _navToDetails(context, data),
|
|
)
|
|
: const SizedBox.shrink();
|
|
},
|
|
),
|
|
));
|
|
}
|
|
),
|
|
),
|
|
BlocBuilder<HomeBloc, HomeState>(
|
|
builder: (context, state) => state.isPaginationLoading
|
|
? const CircularProgressIndicator()
|
|
: const SizedBox.shrink(),
|
|
)
|
|
]));
|
|
}
|
|
|
|
void _navToDetails(BuildContext context, CardData data) {
|
|
Navigator.push(
|
|
context,
|
|
CupertinoPageRoute(builder: (context) => DetailPage((data))),
|
|
);
|
|
}
|
|
|
|
void _showSnackBar(BuildContext context, String title, bool isKnow) {
|
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
|
content: Text(
|
|
'${context.locale.start} ${isKnow ? context.locale.known : context.locale.unknown} ${context.locale.end} $title',
|
|
style: Theme.of(context).textTheme.bodyLarge,
|
|
),
|
|
backgroundColor: Colors.indigoAccent,
|
|
duration: const Duration(seconds: 2),
|
|
));
|
|
});
|
|
}
|
|
|
|
Future<void> _onRefresh() {
|
|
context.read<HomeBloc>().add(HomeLoadDataEvent(search: searchController.text));
|
|
return Future.value(null);
|
|
}
|
|
|
|
void _onKnow(String? id, String title, bool isKnown){
|
|
if (id != null){
|
|
context.read<KnowBloc>().add(ChangeKnowEvent(id));
|
|
_showSnackBar(context, title, !isKnown);
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
// class _CardState extends State<_Card> {
|
|
// bool isKnow = false;
|
|
//
|
|
// @override
|
|
// Widget build(BuildContext context) {
|
|
// return GestureDetector(
|
|
// onTap: widget.onTap,
|
|
// child: Container(
|
|
// constraints: const BoxConstraints(minHeight: 140, maxWidth: 240),
|
|
// padding: const EdgeInsets.all(20),
|
|
// margin: const EdgeInsets.all(10),
|
|
// decoration: BoxDecoration(
|
|
// color: Colors.black12,
|
|
// borderRadius: BorderRadius.circular(10),
|
|
// ),
|
|
// child: LayoutBuilder(builder: (context, constraints) {
|
|
// double imageWidth = constraints.maxWidth;
|
|
// {
|
|
// return Stack(
|
|
// children: [
|
|
// Column(
|
|
// children: [
|
|
// Column(
|
|
// children: [
|
|
// Image.network(widget.image, width: 300),
|
|
// Text(
|
|
// widget.word,
|
|
// style: Theme.of(context).textTheme.headlineLarge,
|
|
// ),
|
|
// Text(
|
|
// widget.translation,
|
|
// style: Theme.of(context).textTheme.bodyMedium,
|
|
// ),
|
|
// ],
|
|
// ),
|
|
// Align(
|
|
// alignment: Alignment.bottomRight,
|
|
// child: Padding(
|
|
// padding: const EdgeInsets.only(left: 8.0),
|
|
// child: GestureDetector(
|
|
// onTap: () => onKnow?.call(id, text, isKnown),
|
|
// child: AnimatedSwitcher(
|
|
// duration: const Duration(milliseconds: 200),
|
|
// child: isKnow
|
|
// ? const Icon(Icons.check_circle,
|
|
// color: Colors.green, key: ValueKey(0))
|
|
// : const Icon(Icons.check_circle_outline, key: ValueKey(1)),
|
|
// ),
|
|
// ),
|
|
// ),
|
|
// ),
|
|
// ],
|
|
// ),
|
|
// Align(
|
|
// alignment: Alignment.center,
|
|
// )
|
|
// ],
|
|
// );
|
|
// }
|
|
// })),
|
|
// );
|
|
// }
|
|
// }
|