pmu_flutter/lec5/lib/view/home_page/home_page.dart

189 lines
5.1 KiB
Dart
Raw Permalink Normal View History

2024-10-11 19:12:12 +04:00
import 'package:flutter/material.dart';
import 'package:test_app/data/repository/potter_repository.dart';
import 'package:test_app/domain/card.dart';
import 'package:test_app/domain/home.dart';
import 'package:test_app/utils/debounce.dart';
import 'package:test_app/view/details_page/details_page.dart';
import 'package:test_app/view/home_page/home_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> {
final Color _color = Colors.orangeAccent;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: _color,
title: Text(widget.title),
),
body: const Body(),
);
}
}
class Body extends StatefulWidget {
const Body({super.key});
@override
State<Body> createState() => _BodyState();
}
class _BodyState extends State<Body> {
final searchController = TextEditingController();
final scrollController = ScrollController();
final repository = PotterRepository();
int nextPage = 1;
int currentPage = 0;
List<CardData> data = [];
bool isLoading = false;
@override
void initState() {
_loadData();
scrollController.addListener(_onNextPageListener);
super.initState();
}
void _setLoading(bool state) {
setState(() {
isLoading = state;
});
}
void _loadData() async {
_setLoading(true);
final result =
await repository.loadData(q: searchController.text, page: nextPage);
_setData(result);
_setLoading(false);
}
void _setData(HomeData? result) {
nextPage = result?.nextPage ?? 1;
if (currentPage >= (result?.currentPage ?? 1)) {
return;
}
currentPage = result?.currentPage ?? 1;
setState(() {
data.addAll(result?.data ?? []);
});
}
@override
void dispose() {
searchController.dispose();
scrollController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Padding(
padding: const EdgeInsets.all(16),
child: SearchBar(
leading: const Icon(Icons.search),
trailing: [
IconButton(
onPressed: () {
searchController.clear();
data.clear();
currentPage = 0;
nextPage = 1;
_loadData();
},
icon: const Icon(Icons.clear)),
],
controller: searchController,
onChanged: (search) {
Debounce.run(() async {
_setLoading(true);
data.clear();
currentPage = 0;
nextPage = 1;
final result = await repository.loadData(q: search);
if (scrollController.hasClients) {
scrollController.jumpTo(0);
}
_setData(result);
_setLoading(false);
});
},
),
),
Expanded(
child: Stack(
children: [
Positioned.fill(
child: ListView.builder(
controller: scrollController,
padding: const EdgeInsets.only(right: 16, left: 16),
itemCount: data.length,
itemBuilder: (context, index) {
final item = data[index];
return HomeCard.fromData(
item,
onLike: (String title, bool isLiked) =>
_showSnackBar(context, title, isLiked),
onTap: () => _navigateToDetails(context, item),
);
}),
),
Align(
alignment:
data.isEmpty ? Alignment.topCenter : Alignment.bottomCenter,
child: Visibility(
visible: isLoading,
child: const Padding(
padding: EdgeInsets.only(bottom: 32, top: 16),
child: CircularProgressIndicator(),
),
),
),
],
),
),
],
);
}
void _showSnackBar(BuildContext context, String title, bool isLiked) {
WidgetsBinding.instance.addPostFrameCallback((_) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(
'$title is ${isLiked ? 'liked' : 'disliked'}!',
style: Theme.of(context).textTheme.bodyLarge,
),
backgroundColor: Colors.orangeAccent,
duration: const Duration(seconds: 1),
));
});
}
void _navigateToDetails(BuildContext context, CardData data) {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => DetailsPage(data)),
);
}
void _onNextPageListener() {
Debounce.run(() {
if (scrollController.offset >
scrollController.position.maxScrollExtent - 10) {
_loadData();
}
});
}
}