Skip to content

Commit 18f7cdb

Browse files
committed
feat: new search page
1 parent 18a53b4 commit 18f7cdb

File tree

5 files changed

+134
-118
lines changed

5 files changed

+134
-118
lines changed

lib/pages/detail/controller.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
// ignore_for_file: use_build_context_synchronously
2+
13
import 'dart:convert';
24

35
import 'package:desktop_webview_window/desktop_webview_window.dart';

lib/pages/search/pages/search_extension.dart

Lines changed: 11 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import 'package:miru_app/widgets/extension_item_card.dart';
1212
import 'package:miru_app/widgets/infinite_scroller.dart';
1313
import 'package:miru_app/widgets/messenger.dart';
1414
import 'package:miru_app/widgets/platform_widget.dart';
15+
import 'package:miru_app/widgets/search_appbar.dart';
1516

1617
class SearchExtensionPage extends fluent.StatefulWidget {
1718
const SearchExtensionPage({
@@ -30,7 +31,6 @@ class SearchExtensionPage extends fluent.StatefulWidget {
3031
class _SearchExtensionPageState extends fluent.State<SearchExtensionPage> {
3132
late ExtensionRuntime _runtime;
3233
late String _keyWord = widget.keyWord ?? '';
33-
bool _showSearh = false;
3434
final List<ExtensionListItem> _data = [];
3535
int _page = 1;
3636
bool _isLoding = true;
@@ -64,6 +64,7 @@ class _SearchExtensionPageState extends fluent.State<SearchExtensionPage> {
6464
_data.addAll(data);
6565
_page++;
6666
} catch (e) {
67+
// ignore: use_build_context_synchronously
6768
showPlatformSnackbar(
6869
context: context,
6970
content: e.toString(),
@@ -88,39 +89,15 @@ class _SearchExtensionPageState extends fluent.State<SearchExtensionPage> {
8889

8990
Widget _buildAndroid(BuildContext context) {
9091
return Scaffold(
91-
appBar: AppBar(
92-
title: _showSearh || _keyWord.isNotEmpty
93-
? TextField(
94-
decoration: InputDecoration(
95-
hintText: 'search.hint-text'.i18n,
96-
border: InputBorder.none,
97-
),
98-
controller: TextEditingController(
99-
text: _keyWord,
100-
),
101-
onChanged: (value) {
102-
if (value.isEmpty) {
103-
_onSearch(value);
104-
}
105-
},
106-
onSubmitted: _onSearch,
107-
)
108-
: Text(
109-
_runtime.extension.name,
110-
),
111-
actions: [
112-
IconButton(
113-
icon: Icon(_showSearh ? Icons.close : Icons.search),
114-
onPressed: () {
115-
setState(() {
116-
if (_showSearh) {
117-
_keyWord = '';
118-
}
119-
_showSearh = !_showSearh;
120-
});
121-
},
122-
),
123-
],
92+
appBar: SearchAppBar(
93+
title: _runtime.extension.name,
94+
textEditingController: TextEditingController(text: _keyWord),
95+
onChanged: (value) {
96+
if (value.isEmpty) {
97+
_onSearch(value);
98+
}
99+
},
100+
onSubmitted: _onSearch,
124101
),
125102
body: InfiniteScroller(
126103
onRefresh: _onRefresh,

lib/pages/search/view.dart

Lines changed: 43 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import 'package:miru_app/pages/search/widgets/search_all_extension.dart';
88
import 'package:miru_app/router/router.dart';
99
import 'package:miru_app/utils/i18n.dart';
1010
import 'package:miru_app/widgets/platform_widget.dart';
11+
import 'package:miru_app/widgets/search_appbar.dart';
1112

1213
class SearchPage extends StatefulWidget {
1314
const SearchPage({Key? key}) : super(key: key);
@@ -26,14 +27,11 @@ class _SearchPageState extends State<SearchPage> {
2627
}
2728

2829
Widget _buildAndroidSearch(BuildContext context) {
29-
return Scaffold(
30-
appBar: AppBar(
31-
title: TextField(
32-
decoration: InputDecoration(
33-
hintText: 'search.hint-text'.i18n,
34-
border: InputBorder.none,
35-
),
36-
controller: TextEditingController(
30+
return DefaultTabController(
31+
length: 4,
32+
child: Scaffold(
33+
appBar: SearchAppBar(
34+
textEditingController: TextEditingController(
3735
text: c.search.value,
3836
),
3937
onChanged: (value) {
@@ -44,86 +42,47 @@ class _SearchPageState extends State<SearchPage> {
4442
onSubmitted: (value) {
4543
c.search.value = value;
4644
},
47-
),
48-
flexibleSpace: Obx(
49-
() => Column(
50-
children: [
51-
if (c.finishCount != c.searchResultList.length)
52-
Padding(
53-
padding: const EdgeInsets.symmetric(horizontal: 16),
54-
child: LinearProgressIndicator(
55-
value: (c.finishCount / c.searchResultList.length),
56-
minHeight: 2,
45+
hintText: "search.hint-text".i18n,
46+
title: "common.search".i18n,
47+
flexibleSpace: Obx(
48+
() => Column(
49+
children: [
50+
if (c.finishCount != c.searchResultList.length)
51+
Padding(
52+
padding: const EdgeInsets.symmetric(horizontal: 16),
53+
child: LinearProgressIndicator(
54+
value: (c.finishCount / c.searchResultList.length),
55+
minHeight: 2,
56+
),
5757
),
58-
),
58+
],
59+
),
60+
),
61+
bottom: TabBar(
62+
tabs: [
63+
Tab(text: 'search.all'.i18n),
64+
Tab(text: 'extension-type.video'.i18n),
65+
Tab(text: 'extension-type.comic'.i18n),
66+
Tab(text: 'extension-type.novel'.i18n),
5967
],
68+
onTap: (value) {
69+
switch (value) {
70+
case 0:
71+
c.getRuntime();
72+
break;
73+
case 1:
74+
c.getRuntime(type: ExtensionType.bangumi);
75+
break;
76+
case 2:
77+
c.getRuntime(type: ExtensionType.manga);
78+
break;
79+
case 3:
80+
c.getRuntime(type: ExtensionType.fikushon);
81+
break;
82+
}
83+
},
6084
),
6185
),
62-
),
63-
body: NestedScrollView(
64-
headerSliverBuilder: (context, innerBoxIsScrolled) {
65-
return [
66-
SliverAppBar(
67-
flexibleSpace: Obx(
68-
() => SizedBox(
69-
height: 60,
70-
child: ListView(
71-
padding: const EdgeInsets.symmetric(horizontal: 16),
72-
scrollDirection: Axis.horizontal,
73-
children: [
74-
ChoiceChip(
75-
label: Text('search.all'.i18n),
76-
selected: c.cuurentExtensionType.value == null,
77-
onSelected: (value) {
78-
if (value) {
79-
c.getRuntime();
80-
}
81-
},
82-
),
83-
const SizedBox(width: 8),
84-
ChoiceChip(
85-
label: Text('extension-type.video'.i18n),
86-
selected: c.cuurentExtensionType.value ==
87-
ExtensionType.bangumi,
88-
onSelected: (value) {
89-
if (value) {
90-
c.getRuntime(type: ExtensionType.bangumi);
91-
}
92-
},
93-
),
94-
const SizedBox(width: 8),
95-
ChoiceChip(
96-
label: Text('extension-type.comic'.i18n),
97-
selected:
98-
c.cuurentExtensionType.value == ExtensionType.manga,
99-
onSelected: (value) {
100-
if (value) {
101-
c.getRuntime(type: ExtensionType.manga);
102-
}
103-
},
104-
),
105-
const SizedBox(width: 8),
106-
ChoiceChip(
107-
label: Text('extension-type.novel'.i18n),
108-
selected: c.cuurentExtensionType.value ==
109-
ExtensionType.fikushon,
110-
onSelected: (value) {
111-
if (value) {
112-
c.getRuntime(
113-
type: ExtensionType.fikushon,
114-
);
115-
}
116-
},
117-
),
118-
],
119-
),
120-
),
121-
),
122-
floating: true,
123-
snap: true,
124-
)
125-
];
126-
},
12786
body: Obx(
12887
() {
12988
// ignore: invalid_use_of_protected_member

lib/utils/extension.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ class ExtensionUtils {
8989
// 保存文件
9090
File(savePath).writeAsStringSync(res.data!);
9191
} catch (e) {
92+
// ignore: use_build_context_synchronously
9293
showPlatformDialog(
9394
context: context,
9495
title: 'extension-install-error'.i18n,

lib/widgets/search_appbar.dart

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import 'package:flutter/material.dart';
2+
3+
class SearchAppBar extends StatefulWidget implements PreferredSizeWidget {
4+
SearchAppBar({
5+
super.key,
6+
required this.title,
7+
required this.textEditingController,
8+
this.actions,
9+
this.bottom,
10+
this.flexibleSpace,
11+
this.toolbarHeight,
12+
this.onSubmitted,
13+
this.onChanged,
14+
this.hintText,
15+
}) : preferredSize =
16+
_PreferredAppBarSize(toolbarHeight, bottom?.preferredSize.height);
17+
final String title;
18+
final List<Widget>? actions;
19+
final PreferredSizeWidget? bottom;
20+
final Widget? flexibleSpace;
21+
final double? toolbarHeight;
22+
final TextEditingController textEditingController;
23+
final Function(String)? onSubmitted;
24+
final Function(String)? onChanged;
25+
final String? hintText;
26+
27+
@override
28+
State<SearchAppBar> createState() => _SearchAppBarState();
29+
30+
@override
31+
final Size preferredSize;
32+
}
33+
34+
class _SearchAppBarState extends State<SearchAppBar> {
35+
late bool _showSearch = widget.textEditingController.text.isNotEmpty;
36+
37+
@override
38+
Widget build(BuildContext context) {
39+
return AppBar(
40+
title: _showSearch
41+
? TextField(
42+
controller: widget.textEditingController,
43+
decoration: InputDecoration(
44+
hintText: widget.hintText ?? widget.title,
45+
border: InputBorder.none,
46+
),
47+
autofocus: true,
48+
onChanged: widget.onChanged,
49+
onSubmitted: widget.onSubmitted,
50+
)
51+
: Text(widget.title),
52+
actions: [
53+
IconButton(
54+
onPressed: () {
55+
setState(() {
56+
_showSearch = !_showSearch;
57+
if (!_showSearch) widget.onSubmitted?.call("");
58+
});
59+
},
60+
icon: Icon(_showSearch ? Icons.close : Icons.search),
61+
),
62+
...widget.actions ?? [],
63+
],
64+
bottom: widget.bottom,
65+
flexibleSpace: widget.flexibleSpace,
66+
);
67+
}
68+
}
69+
70+
class _PreferredAppBarSize extends Size {
71+
_PreferredAppBarSize(this.toolbarHeight, this.bottomHeight)
72+
: super.fromHeight(
73+
(toolbarHeight ?? kToolbarHeight) + (bottomHeight ?? 0));
74+
75+
final double? toolbarHeight;
76+
final double? bottomHeight;
77+
}

0 commit comments

Comments
 (0)