Skip to content

Commit 4478ed2

Browse files
committed
feat: anilist search for anime and manga
1 parent f8d6065 commit 4478ed2

File tree

17 files changed

+590
-256
lines changed

17 files changed

+590
-256
lines changed

lib/Adaptor/Media/MediaAdaptor.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ class MediaGridState extends State<MediaAdaptor> {
5656

5757
@override
5858
Widget build(BuildContext context) {
59+
if (_mediaList.isEmpty) {
60+
return const SizedBox();
61+
}
5962
switch (widget.type) {
6063
case 0:
6164
return _buildHorizontalList();

lib/DataClass/SearchResults.dart

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ part 'Data/SearchResults.g.dart';
66

77
@JsonSerializable()
88
class SearchResults {
9-
final String type;
10-
bool isAdult;
9+
String type;
10+
bool? isAdult;
1111
bool? onList;
1212
int? perPage;
1313
String? search;
@@ -23,16 +23,17 @@ class SearchResults {
2323
int? seasonYear;
2424
int? startYear;
2525
String? season;
26-
int page;
27-
List<Media> results;
28-
bool hasNextPage;
29-
26+
int? page;
27+
List<Media>? results;
28+
bool? hasNextPage;
29+
int? id;
30+
bool? hdCover;
3031
SearchResults({
3132
required this.type,
32-
required this.isAdult,
33+
this.isAdult,
3334
this.onList,
3435
this.perPage,
35-
this.search,
36+
this.search = "",
3637
this.countryOfOrigin,
3738
this.sort,
3839
this.genres,
@@ -46,8 +47,10 @@ class SearchResults {
4647
this.startYear,
4748
this.season,
4849
this.page = 1,
49-
required this.results,
50-
required this.hasNextPage,
50+
this.results = const [],
51+
this.hasNextPage = false,
52+
this.hdCover,
53+
this.id,
5154
});
5255

5356
List<SearchChip> toChipList() {

lib/Screens/Detail/Tabs/Watch/Anime/AnimeWatchScreen.dart

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import 'package:collection/collection.dart';
22
import 'package:dantotsu/DataClass/Media.dart';
3-
import 'package:dantotsu/Functions/Extensions.dart';
4-
import 'package:dantotsu/Preferences/PrefManager.dart';
53
import 'package:dantotsu/Screens/Detail/Tabs/Watch/BaseParser.dart';
64
import 'package:dantotsu/Screens/Detail/Tabs/Watch/BaseWatchScreen.dart';
75
import 'package:flutter/material.dart';

lib/Screens/Search/SearchScreen.dart

Lines changed: 231 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,244 @@
1+
import 'dart:async';
2+
3+
import 'package:dantotsu/Functions/Extensions.dart';
4+
import 'package:dantotsu/Preferences/PrefManager.dart';
5+
import 'package:dantotsu/Services/Screens/BaseSearchScreen.dart';
16
import 'package:flutter/material.dart';
7+
import 'package:get/get.dart';
8+
import 'package:icons_plus/icons_plus.dart';
9+
import 'package:provider/provider.dart';
10+
11+
import '../../DataClass/SearchResults.dart';
12+
import '../../Services/MediaService.dart';
13+
import '../../Theme/Colors.dart';
14+
import '../../Theme/ThemeProvider.dart';
15+
import '../../Widgets/ScrollConfig.dart';
216

317
class SearchScreen extends StatefulWidget {
4-
const SearchScreen({super.key});
18+
final String title;
19+
final SearchResults? args;
20+
21+
const SearchScreen({super.key, required this.title, this.args});
522

623
@override
724
SearchScreenState createState() => SearchScreenState();
825
}
926

1027
class SearchScreenState extends State<SearchScreen> {
28+
Timer? _debounce;
29+
late MediaService service;
30+
31+
@override
32+
void dispose() {
33+
super.dispose();
34+
_debounce?.cancel();
35+
var screen = service.searchScreen;
36+
if (screen != null) screen.remove();
37+
}
38+
39+
void _onSearchChanged(String value) {
40+
if (_debounce?.isActive ?? false) _debounce!.cancel();
41+
_debounce = Timer(const Duration(milliseconds: 500), () {
42+
var screen = service.searchScreen;
43+
var key = "${service.getName}${widget.title}_searchHistory";
44+
List<String> searchHistory = loadCustomData(key) ?? [];
45+
if (!searchHistory.contains(value.trim().toLowerCase()) &&
46+
value.isNotEmpty) {
47+
var list = List<String>.from(searchHistory);
48+
list.add(value.trim().toLowerCase());
49+
saveCustomData(key, list);
50+
}
51+
screen?.searchQuery.value = value;
52+
screen?.searchResults.value = screen.searchResults.value..search = value;
53+
if (value.isNotEmpty) screen?.search();
54+
});
55+
}
56+
57+
@override
58+
void initState() {
59+
super.initState();
60+
service = context.currentService(listen: false);
61+
var screen = service.searchScreen;
62+
if (screen != null) screen.init(s: widget.args);
63+
}
64+
1165
@override
1266
Widget build(BuildContext context) {
13-
return Container();
67+
var screen = service.searchScreen;
68+
if (screen == null) {
69+
return service.notImplemented(widget.runtimeType.toString());
70+
}
71+
screen.init();
72+
return Scaffold(
73+
body: Stack(
74+
children: [
75+
_buildContent(screen),
76+
_buildScrollToTopButton(screen),
77+
],
78+
),
79+
);
80+
}
81+
82+
Widget _buildContent(BaseSearchScreen screen) {
83+
var key = "${service.getName}${widget.title}_searchHistory";
84+
var theme = context.theme.colorScheme;
85+
List<String> searchHistory = loadCustomData(key) ?? [];
86+
87+
return CustomScrollConfig(
88+
context,
89+
controller: screen.scrollController,
90+
children: [
91+
SliverToBoxAdapter(child: _buildSearchBar()),
92+
SliverList(
93+
delegate: SliverChildListDelegate([
94+
Padding(
95+
padding: const EdgeInsets.symmetric(vertical: 8),
96+
child: Obx(() {
97+
if (screen.searchQuery.value.isEmpty) {
98+
return ListView.builder(
99+
shrinkWrap: true,
100+
physics: const NeverScrollableScrollPhysics(),
101+
itemCount: searchHistory.length,
102+
itemBuilder: (context, index) {
103+
return Padding(
104+
padding: const EdgeInsets.symmetric(
105+
vertical: 4.0,
106+
horizontal: 24.0,
107+
),
108+
child: Card(
109+
elevation: 3,
110+
shape: RoundedRectangleBorder(
111+
borderRadius: BorderRadius.circular(10),
112+
),
113+
child: ListTile(
114+
title: Text(
115+
searchHistory[index],
116+
style: TextStyle(
117+
color: theme.onSurface,
118+
fontWeight: FontWeight.bold,
119+
),
120+
),
121+
trailing: IconButton(
122+
icon: Icon(
123+
FontAwesome.trash_solid,
124+
size: 18,
125+
),
126+
onPressed: () {
127+
setState(() {
128+
searchHistory.removeAt(index);
129+
saveCustomData(key, searchHistory);
130+
});
131+
},
132+
),
133+
onTap: () {
134+
_searchController.text = searchHistory[index];
135+
_onSearchChanged(searchHistory[index]);
136+
},
137+
),
138+
),
139+
);
140+
},
141+
);
142+
} else {
143+
return Column(
144+
children: [
145+
...screen.searchWidget(context),
146+
SizedBox(
147+
height: 64,
148+
child: Center(
149+
child: !service.searchScreen!.loadMore.value &&
150+
service.searchScreen!.canLoadMore.value
151+
? const CircularProgressIndicator()
152+
: const SizedBox(height: 64),
153+
),
154+
),
155+
],
156+
);
157+
}
158+
}),
159+
),
160+
]),
161+
),
162+
],
163+
);
164+
}
165+
166+
final TextEditingController _searchController = TextEditingController();
167+
168+
Widget _buildSearchBar() {
169+
var theme = context.theme.colorScheme;
170+
return Column(
171+
mainAxisSize: MainAxisSize.min,
172+
children: [
173+
Padding(
174+
padding: EdgeInsets.only(top: 24.statusBar(), left: 24, right: 24),
175+
child: Row(
176+
children: [
177+
IconButton(
178+
onPressed: () => Get.back(),
179+
icon: Icon(Icons.arrow_back_ios),
180+
),
181+
Expanded(
182+
child: TextField(
183+
style: const TextStyle(
184+
fontFamily: 'Poppins',
185+
fontWeight: FontWeight.bold,
186+
fontSize: 14.0,
187+
),
188+
controller: _searchController,
189+
decoration: InputDecoration(
190+
hintText: widget.title,
191+
hintStyle: const TextStyle(
192+
fontFamily: 'Poppins',
193+
fontWeight: FontWeight.bold,
194+
fontSize: 14.0,
195+
),
196+
suffixIcon: Icon(Icons.search, color: theme.onSurface),
197+
border: OutlineInputBorder(
198+
borderRadius: BorderRadius.circular(28),
199+
),
200+
enabledBorder: OutlineInputBorder(
201+
borderRadius: BorderRadius.circular(28),
202+
borderSide: BorderSide(
203+
color: theme.primaryContainer,
204+
width: 2.0,
205+
),
206+
),
207+
filled: true,
208+
fillColor: Colors.grey.withOpacity(0.2),
209+
),
210+
onChanged: _onSearchChanged,
211+
),
212+
),
213+
],
214+
),
215+
),
216+
],
217+
);
218+
}
219+
220+
Widget _buildScrollToTopButton(BaseSearchScreen service) {
221+
var theme = Provider.of<ThemeNotifier>(context);
222+
return Positioned(
223+
bottom: 32.bottomBar(),
224+
left: (0.screenWidthWithContext(context) / 2) - 24.0,
225+
child: Obx(() => service.scrollToTop.value
226+
? Container(
227+
decoration: BoxDecoration(
228+
color: theme.isDarkMode ? greyNavDark : greyNavLight,
229+
borderRadius: BorderRadius.circular(64.0),
230+
),
231+
padding: const EdgeInsets.all(4.0),
232+
child: IconButton(
233+
icon: const Icon(Icons.arrow_upward),
234+
onPressed: () => service.scrollController.animateTo(
235+
0,
236+
duration: const Duration(milliseconds: 500),
237+
curve: Curves.easeInOut,
238+
),
239+
),
240+
)
241+
: const SizedBox()),
242+
);
14243
}
15244
}

lib/Services/Api/Queries.dart

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -71,26 +71,5 @@ abstract class Queries {
7171
/// - [hd], [adultOnly]: Additional display and filtering options.
7272
///
7373
/// Returns a [SearchResults] object containing the search results.
74-
Future<SearchResults?> search({
75-
required String type,
76-
int? page,
77-
int? perPage,
78-
String? search,
79-
String? sort,
80-
List<String>? genres,
81-
List<String>? tags,
82-
String? status,
83-
String? source,
84-
String? format,
85-
String? countryOfOrigin,
86-
bool isAdult = false,
87-
bool? onList,
88-
List<String>? excludedGenres,
89-
List<String>? excludedTags,
90-
int? startYear,
91-
int? seasonYear,
92-
String? season,
93-
int? id,
94-
bool hd = false,
95-
});
74+
Future<SearchResults?> search(SearchResults? searchResults);
9675
}

lib/Services/Screens/BaseMediaScreen.dart

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,6 @@ abstract class BaseMediaScreen extends GetxController {
5454
loadMore.value = false;
5555
if (canLoadMore.value) {
5656
await loadNextPage();
57-
} else {
58-
snackString('DAMN! YOU TRULY ARE JOBLESS\nYOU REACHED THE END');
5957
}
6058
}
6159
scrollToTop.value = _canScroll();

0 commit comments

Comments
 (0)