Showing
13 changed files
with
478 additions
and
78 deletions
assets/images/poem/poem_delete.png
0 → 100644

1.15 KB
assets/images/poem/poem_search.png
0 → 100644

955 Bytes
... | @@ -95,18 +95,18 @@ class JsonConvert { | ... | @@ -95,18 +95,18 @@ class JsonConvert { |
95 | } | 95 | } |
96 | 96 | ||
97 | //list is returned by type | 97 | //list is returned by type |
98 | - static M? _getListChildType<M>(List<dynamic> data) { | 98 | + static M? _getListChildType<M>(List<Map<String, dynamic>> data) { |
99 | if(<UserEntity>[] is M){ | 99 | if(<UserEntity>[] is M){ |
100 | - return data.map<UserEntity>((e) => UserEntity.fromJson(e)).toList() as M; | 100 | + return data.map<UserEntity>((Map<String, dynamic> e) => UserEntity.fromJson(e)).toList() as M; |
101 | } | 101 | } |
102 | if(<CategoryItemEntity>[] is M){ | 102 | if(<CategoryItemEntity>[] is M){ |
103 | - return data.map<CategoryItemEntity>((e) => CategoryItemEntity.fromJson(e)).toList() as M; | 103 | + return data.map<CategoryItemEntity>((Map<String, dynamic> e) => CategoryItemEntity.fromJson(e)).toList() as M; |
104 | } | 104 | } |
105 | if(<FriendEntity>[] is M){ | 105 | if(<FriendEntity>[] is M){ |
106 | - return data.map<FriendEntity>((e) => FriendEntity.fromJson(e)).toList() as M; | 106 | + return data.map<FriendEntity>((Map<String, dynamic> e) => FriendEntity.fromJson(e)).toList() as M; |
107 | } | 107 | } |
108 | if(<FriendData>[] is M){ | 108 | if(<FriendData>[] is M){ |
109 | - return data.map<FriendData>((e) => FriendData.fromJson(e)).toList() as M; | 109 | + return data.map<FriendData>((Map<String, dynamic> e) => FriendData.fromJson(e)).toList() as M; |
110 | } | 110 | } |
111 | 111 | ||
112 | print("${M.toString()} not found"); | 112 | print("${M.toString()} not found"); |
... | @@ -117,9 +117,8 @@ class JsonConvert { | ... | @@ -117,9 +117,8 @@ class JsonConvert { |
117 | static M? fromJsonAsT<M>(dynamic json) { | 117 | static M? fromJsonAsT<M>(dynamic json) { |
118 | if(json == null){ | 118 | if(json == null){ |
119 | return null; | 119 | return null; |
120 | - } | 120 | + } if (json is List) { |
121 | - if (json is List) { | 121 | + return _getListChildType<M>(json.map((e) => e as Map<String, dynamic>).toList()); |
122 | - return _getListChildType<M>(json); | ||
123 | } else { | 122 | } else { |
124 | return _fromJsonSingle<M>(json as Map<String, dynamic>); | 123 | return _fromJsonSingle<M>(json as Map<String, dynamic>); |
125 | } | 124 | } | ... | ... |
lib/generated/json/search_entity.g.dart
0 → 100644
This diff is collapsed. Click to expand it.
1 | - | ||
2 | - | ||
3 | import 'base_page.dart'; | 1 | import 'base_page.dart'; |
4 | import 'base_page_presenter.dart'; | 2 | import 'base_page_presenter.dart'; |
5 | import 'base_presenter.dart'; | 3 | import 'base_presenter.dart'; |
6 | 4 | ||
7 | /// 管理多个Presenter,实现复用。 | 5 | /// 管理多个Presenter,实现复用。 |
8 | class PowerPresenter<IMvpView> extends BasePresenter { | 6 | class PowerPresenter<IMvpView> extends BasePresenter { |
9 | - | ||
10 | PowerPresenter(BasePageMixin state) { | 7 | PowerPresenter(BasePageMixin state) { |
11 | _state = state; | 8 | _state = state; |
12 | } | 9 | } |
... | @@ -22,7 +19,7 @@ class PowerPresenter<IMvpView> extends BasePresenter { | ... | @@ -22,7 +19,7 @@ class PowerPresenter<IMvpView> extends BasePresenter { |
22 | void _requestPresenter(BasePagePresenter presenter) { | 19 | void _requestPresenter(BasePagePresenter presenter) { |
23 | presenter.view = _state; | 20 | presenter.view = _state; |
24 | } | 21 | } |
25 | - | 22 | + |
26 | @override | 23 | @override |
27 | void deactivate() { | 24 | void deactivate() { |
28 | _presenters.forEach(_deactivate); | 25 | _presenters.forEach(_deactivate); |
... | @@ -43,10 +40,10 @@ class PowerPresenter<IMvpView> extends BasePresenter { | ... | @@ -43,10 +40,10 @@ class PowerPresenter<IMvpView> extends BasePresenter { |
43 | 40 | ||
44 | @override | 41 | @override |
45 | void didUpdateWidgets<W>(W oldWidget) { | 42 | void didUpdateWidgets<W>(W oldWidget) { |
46 | - | ||
47 | void _didUpdateWidgets(BasePagePresenter presenter) { | 43 | void _didUpdateWidgets(BasePagePresenter presenter) { |
48 | presenter.didUpdateWidgets<W>(oldWidget); | 44 | presenter.didUpdateWidgets<W>(oldWidget); |
49 | } | 45 | } |
46 | + | ||
50 | _presenters.forEach(_didUpdateWidgets); | 47 | _presenters.forEach(_didUpdateWidgets); |
51 | } | 48 | } |
52 | 49 | ||
... | @@ -67,5 +64,4 @@ class PowerPresenter<IMvpView> extends BasePresenter { | ... | @@ -67,5 +64,4 @@ class PowerPresenter<IMvpView> extends BasePresenter { |
67 | void _initState(BasePagePresenter presenter) { | 64 | void _initState(BasePagePresenter presenter) { |
68 | presenter.initState(); | 65 | presenter.initState(); |
69 | } | 66 | } |
70 | - | ||
71 | } | 67 | } | ... | ... |
lib/poem/iview/poem_search_iview.dart
0 → 100644
lib/poem/models/search_entity.dart
0 → 100644
1 | +import 'package:one_poem/generated/json/base/json_field.dart'; | ||
2 | +import 'package:one_poem/generated/json/search_entity.g.dart'; | ||
3 | + | ||
4 | +@JsonSerializable() | ||
5 | +class SearchEntity { | ||
6 | + | ||
7 | + SearchEntity(); | ||
8 | + | ||
9 | + factory SearchEntity.fromJson(Map<String, dynamic> json) => $SearchEntityFromJson(json); | ||
10 | + | ||
11 | + Map<String, dynamic> toJson() => $SearchEntityToJson(this); | ||
12 | + | ||
13 | + @JSONField(name: 'total_count') | ||
14 | + int? totalCount; | ||
15 | + @JSONField(name: 'incomplete_results') | ||
16 | + bool? incompleteResults; | ||
17 | + List<SearchItems>? items; | ||
18 | +} | ||
19 | + | ||
20 | +@JsonSerializable() | ||
21 | +class SearchItems { | ||
22 | + | ||
23 | + SearchItems(); | ||
24 | + | ||
25 | + factory SearchItems.fromJson(Map<String, dynamic> json) => $SearchItemsFromJson(json); | ||
26 | + | ||
27 | + Map<String, dynamic> toJson() => $SearchItemsToJson(this); | ||
28 | + | ||
29 | + int? id; | ||
30 | + @JSONField(name: 'node_id') | ||
31 | + String? nodeId; | ||
32 | + String? name; | ||
33 | + @JSONField(name: 'full_name') | ||
34 | + String? fullName; | ||
35 | + bool? private; | ||
36 | + SearchItemsOwner? owner; | ||
37 | + @JSONField(name: 'html_url') | ||
38 | + String? htmlUrl; | ||
39 | + String? description; | ||
40 | + bool? fork; | ||
41 | + String? url; | ||
42 | + @JSONField(name: 'forks_url') | ||
43 | + String? forksUrl; | ||
44 | + @JSONField(name: 'keys_url') | ||
45 | + String? keysUrl; | ||
46 | + @JSONField(name: 'collaborators_url') | ||
47 | + String? collaboratorsUrl; | ||
48 | + @JSONField(name: 'teams_url') | ||
49 | + String? teamsUrl; | ||
50 | + @JSONField(name: 'hooks_url') | ||
51 | + String? hooksUrl; | ||
52 | + @JSONField(name: 'issue_events_url') | ||
53 | + String? issueEventsUrl; | ||
54 | + @JSONField(name: 'events_url') | ||
55 | + String? eventsUrl; | ||
56 | + @JSONField(name: 'assignees_url') | ||
57 | + String? assigneesUrl; | ||
58 | + @JSONField(name: 'branches_url') | ||
59 | + String? branchesUrl; | ||
60 | + @JSONField(name: 'tags_url') | ||
61 | + String? tagsUrl; | ||
62 | + @JSONField(name: 'blobs_url') | ||
63 | + String? blobsUrl; | ||
64 | + @JSONField(name: 'git_tags_url') | ||
65 | + String? gitTagsUrl; | ||
66 | + @JSONField(name: 'git_refs_url') | ||
67 | + String? gitRefsUrl; | ||
68 | + @JSONField(name: 'trees_url') | ||
69 | + String? treesUrl; | ||
70 | + @JSONField(name: 'statuses_url') | ||
71 | + String? statusesUrl; | ||
72 | + @JSONField(name: 'languages_url') | ||
73 | + String? languagesUrl; | ||
74 | + @JSONField(name: 'stargazers_url') | ||
75 | + String? stargazersUrl; | ||
76 | + @JSONField(name: 'contributors_url') | ||
77 | + String? contributorsUrl; | ||
78 | + @JSONField(name: 'subscribers_url') | ||
79 | + String? subscribersUrl; | ||
80 | + @JSONField(name: 'subscription_url') | ||
81 | + String? subscriptionUrl; | ||
82 | + @JSONField(name: 'commits_url') | ||
83 | + String? commitsUrl; | ||
84 | + @JSONField(name: 'git_commits_url') | ||
85 | + String? gitCommitsUrl; | ||
86 | + @JSONField(name: 'comments_url') | ||
87 | + String? commentsUrl; | ||
88 | + @JSONField(name: 'issue_comment_url') | ||
89 | + String? issueCommentUrl; | ||
90 | + @JSONField(name: 'contents_url') | ||
91 | + String? contentsUrl; | ||
92 | + @JSONField(name: 'compare_url') | ||
93 | + String? compareUrl; | ||
94 | + @JSONField(name: 'merges_url') | ||
95 | + String? mergesUrl; | ||
96 | + @JSONField(name: 'archive_url') | ||
97 | + String? archiveUrl; | ||
98 | + @JSONField(name: 'downloads_url') | ||
99 | + String? downloadsUrl; | ||
100 | + @JSONField(name: 'issues_url') | ||
101 | + String? issuesUrl; | ||
102 | + @JSONField(name: 'pulls_url') | ||
103 | + String? pullsUrl; | ||
104 | + @JSONField(name: 'milestones_url') | ||
105 | + String? milestonesUrl; | ||
106 | + @JSONField(name: 'notifications_url') | ||
107 | + String? notificationsUrl; | ||
108 | + @JSONField(name: 'labels_url') | ||
109 | + String? labelsUrl; | ||
110 | + @JSONField(name: 'releases_url') | ||
111 | + String? releasesUrl; | ||
112 | + @JSONField(name: 'deployments_url') | ||
113 | + String? deploymentsUrl; | ||
114 | + @JSONField(name: 'created_at') | ||
115 | + String? createdAt; | ||
116 | + @JSONField(name: 'updated_at') | ||
117 | + String? updatedAt; | ||
118 | + @JSONField(name: 'pushed_at') | ||
119 | + String? pushedAt; | ||
120 | + @JSONField(name: 'git_url') | ||
121 | + String? gitUrl; | ||
122 | + @JSONField(name: 'ssh_url') | ||
123 | + String? sshUrl; | ||
124 | + @JSONField(name: 'clone_url') | ||
125 | + String? cloneUrl; | ||
126 | + @JSONField(name: 'svn_url') | ||
127 | + String? svnUrl; | ||
128 | + String? homepage; | ||
129 | + int? size; | ||
130 | + @JSONField(name: 'stargazers_count') | ||
131 | + int? stargazersCount; | ||
132 | + @JSONField(name: 'watchers_count') | ||
133 | + int? watchersCount; | ||
134 | + String? language; | ||
135 | + @JSONField(name: 'has_issues') | ||
136 | + bool? hasIssues; | ||
137 | + @JSONField(name: 'has_projects') | ||
138 | + bool? hasProjects; | ||
139 | + @JSONField(name: 'has_downloads') | ||
140 | + bool? hasDownloads; | ||
141 | + @JSONField(name: 'has_wiki') | ||
142 | + bool? hasWiki; | ||
143 | + @JSONField(name: 'has_pages') | ||
144 | + bool? hasPages; | ||
145 | + @JSONField(name: 'forks_count') | ||
146 | + int? forksCount; | ||
147 | + bool? archived; | ||
148 | + bool? disabled; | ||
149 | + @JSONField(name: 'open_issues_count') | ||
150 | + int? openIssuesCount; | ||
151 | + SearchItemsLicense? license; | ||
152 | + int? forks; | ||
153 | + @JSONField(name: 'open_issues') | ||
154 | + int? openIssues; | ||
155 | + int? watchers; | ||
156 | + @JSONField(name: 'default_branch') | ||
157 | + String? defaultBranch; | ||
158 | + double? score; | ||
159 | +} | ||
160 | + | ||
161 | +@JsonSerializable() | ||
162 | +class SearchItemsOwner { | ||
163 | + | ||
164 | + SearchItemsOwner(); | ||
165 | + | ||
166 | + factory SearchItemsOwner.fromJson(Map<String, dynamic> json) => $SearchItemsOwnerFromJson(json); | ||
167 | + | ||
168 | + Map<String, dynamic> toJson() => $SearchItemsOwnerToJson(this); | ||
169 | + | ||
170 | + String? login; | ||
171 | + int? id; | ||
172 | + @JSONField(name: 'node_id') | ||
173 | + String? nodeId; | ||
174 | + @JSONField(name: 'avatar_url') | ||
175 | + String? avatarUrl; | ||
176 | + @JSONField(name: 'gravatar_id') | ||
177 | + String? gravatarId; | ||
178 | + String? url; | ||
179 | + @JSONField(name: 'html_url') | ||
180 | + String? htmlUrl; | ||
181 | + @JSONField(name: 'followers_url') | ||
182 | + String? followersUrl; | ||
183 | + @JSONField(name: 'following_url') | ||
184 | + String? followingUrl; | ||
185 | + @JSONField(name: 'gists_url') | ||
186 | + String? gistsUrl; | ||
187 | + @JSONField(name: 'starred_url') | ||
188 | + String? starredUrl; | ||
189 | + @JSONField(name: 'subscriptions_url') | ||
190 | + String? subscriptionsUrl; | ||
191 | + @JSONField(name: 'organizations_url') | ||
192 | + String? organizationsUrl; | ||
193 | + @JSONField(name: 'repos_url') | ||
194 | + String? reposUrl; | ||
195 | + @JSONField(name: 'events_url') | ||
196 | + String? eventsUrl; | ||
197 | + @JSONField(name: 'received_events_url') | ||
198 | + String? receivedEventsUrl; | ||
199 | + String? type; | ||
200 | + @JSONField(name: 'site_admin') | ||
201 | + bool? siteAdmin; | ||
202 | +} | ||
203 | + | ||
204 | +@JsonSerializable() | ||
205 | +class SearchItemsLicense { | ||
206 | + | ||
207 | + SearchItemsLicense(); | ||
208 | + | ||
209 | + factory SearchItemsLicense.fromJson(Map<String, dynamic> json) => $SearchItemsLicenseFromJson(json); | ||
210 | + | ||
211 | + Map<String, dynamic> toJson() => $SearchItemsLicenseToJson(this); | ||
212 | + | ||
213 | + String? key; | ||
214 | + String? name; | ||
215 | + @JSONField(name: 'spdx_id') | ||
216 | + String? spdxId; | ||
217 | + String? url; | ||
218 | + @JSONField(name: 'node_id') | ||
219 | + String? nodeId; | ||
220 | +} |
1 | -import 'package:flutter/cupertino.dart'; | ||
2 | import 'package:flutter/material.dart'; | 1 | import 'package:flutter/material.dart'; |
3 | import 'package:one_poem/category/category_router.dart'; | 2 | import 'package:one_poem/category/category_router.dart'; |
3 | +import 'package:one_poem/poem/poem_router.dart'; | ||
4 | import 'package:one_poem/routers/fluro_navigator.dart'; | 4 | import 'package:one_poem/routers/fluro_navigator.dart'; |
5 | import 'package:one_poem/tiktok/controller/tiktok_video_list_controller.dart'; | 5 | import 'package:one_poem/tiktok/controller/tiktok_video_list_controller.dart'; |
6 | import 'package:one_poem/tiktok/mock/video.dart'; | 6 | import 'package:one_poem/tiktok/mock/video.dart'; |
... | @@ -134,7 +134,10 @@ class _PoemPageState extends State<PoemPage> with WidgetsBindingObserver { | ... | @@ -134,7 +134,10 @@ class _PoemPageState extends State<PoemPage> with WidgetsBindingObserver { |
134 | Icons.search, | 134 | Icons.search, |
135 | color: Colors.white, | 135 | color: Colors.white, |
136 | ), | 136 | ), |
137 | - onPressed: () {}, | 137 | + onPressed: () { |
138 | + NavigatorUtils.push(context, PoemRouter.poemSearchPage); | ||
139 | + _videoListController.currentPlayer.pause(); | ||
140 | + }, | ||
138 | ), | 141 | ), |
139 | ), | 142 | ), |
140 | leftPage: searchPage, | 143 | leftPage: searchPage, | ... | ... |
lib/poem/page/poem_search_page.dart
0 → 100644
1 | +import 'package:flutter/material.dart'; | ||
2 | +import 'package:one_poem/mvp/base_page.dart'; | ||
3 | +import 'package:one_poem/mvp/power_presenter.dart'; | ||
4 | +import 'package:one_poem/poem/iview/poem_search_iview.dart'; | ||
5 | +import 'package:one_poem/poem/models/search_entity.dart'; | ||
6 | +import 'package:one_poem/poem/presenter/poem_search_presenter.dart'; | ||
7 | +import 'package:one_poem/poem/provider/base_list_provider.dart'; | ||
8 | +import 'package:one_poem/widgets/my_refresh_list.dart'; | ||
9 | +import 'package:one_poem/widgets/search_bar.dart'; | ||
10 | +import 'package:one_poem/widgets/state_layout.dart'; | ||
11 | +import 'package:provider/provider.dart'; | ||
12 | + | ||
13 | +import 'package:one_poem/extension/int_extension.dart'; | ||
14 | + | ||
15 | +class PoemSearchPage extends StatefulWidget { | ||
16 | + const PoemSearchPage({Key? key}) : super(key: key); | ||
17 | + | ||
18 | + @override | ||
19 | + _PoemSearchPageState createState() => _PoemSearchPageState(); | ||
20 | +} | ||
21 | + | ||
22 | +class _PoemSearchPageState extends State<PoemSearchPage> | ||
23 | + with BasePageMixin<PoemSearchPage, PowerPresenter> | ||
24 | + implements PoemSearchIMvpView { | ||
25 | + @override | ||
26 | + BaseListProvider<SearchItems> provider = BaseListProvider<SearchItems>(); | ||
27 | + | ||
28 | + late String _keyword; | ||
29 | + int _page = 1; | ||
30 | + | ||
31 | + @override | ||
32 | + void initState() { | ||
33 | + provider.stateType = StateType.empty; | ||
34 | + super.initState(); | ||
35 | + } | ||
36 | + | ||
37 | + @override | ||
38 | + Widget build(BuildContext context) { | ||
39 | + return ChangeNotifierProvider<BaseListProvider<SearchItems>>( | ||
40 | + create: (_) => provider, | ||
41 | + child: Scaffold( | ||
42 | + appBar: SearchBar( | ||
43 | + hintText: '请输入要查询的内容', | ||
44 | + onPressed: (text) { | ||
45 | + if (text.isEmpty) { | ||
46 | + showToast('搜索关键字不能为空!'); | ||
47 | + return; | ||
48 | + } | ||
49 | + _keyword = text; | ||
50 | + provider.setStateType(StateType.loading); | ||
51 | + _page = 1; | ||
52 | + _poemSearchPresenter.search(_keyword, _page, true); | ||
53 | + }, | ||
54 | + ), | ||
55 | + body: | ||
56 | + Consumer<BaseListProvider<SearchItems>>(builder: (_, provider, __) { | ||
57 | + return DeerListView( | ||
58 | + key: const Key('poem_search_list'), | ||
59 | + itemCount: provider.list.length, | ||
60 | + stateType: provider.stateType, | ||
61 | + onRefresh: _onRefresh, | ||
62 | + loadMore: _loadMore, | ||
63 | + itemExtent: 50.0, | ||
64 | + hasMore: provider.hasMore, | ||
65 | + itemBuilder: (_, index) { | ||
66 | + return Container( | ||
67 | + padding: EdgeInsets.symmetric(horizontal: 16.px), | ||
68 | + alignment: Alignment.centerLeft, | ||
69 | + child: Text(provider.list[index].name!), | ||
70 | + ); | ||
71 | + }, | ||
72 | + ); | ||
73 | + }), | ||
74 | + ), | ||
75 | + ); | ||
76 | + } | ||
77 | + | ||
78 | + Future<void> _onRefresh() async { | ||
79 | + _page = 1; | ||
80 | + await _poemSearchPresenter.search(_keyword, _page, false); | ||
81 | + } | ||
82 | + | ||
83 | + Future<void> _loadMore() async { | ||
84 | + _page++; | ||
85 | + await _poemSearchPresenter.search(_keyword, _page, false); | ||
86 | + } | ||
87 | + | ||
88 | + late PoemSearchPresenter _poemSearchPresenter; | ||
89 | + @override | ||
90 | + PowerPresenter createPresenter() { | ||
91 | + final PowerPresenter powerPresenter = PowerPresenter<dynamic>(this); | ||
92 | + _poemSearchPresenter = PoemSearchPresenter(); | ||
93 | + powerPresenter.requestPresenter([_poemSearchPresenter]); | ||
94 | + return powerPresenter; | ||
95 | + } | ||
96 | +} |
1 | import 'package:fluro/fluro.dart'; | 1 | import 'package:fluro/fluro.dart'; |
2 | import 'package:one_poem/poem/page/poem_record_audio.dart'; | 2 | import 'package:one_poem/poem/page/poem_record_audio.dart'; |
3 | +import 'package:one_poem/poem/page/poem_search_page.dart'; | ||
3 | import 'package:one_poem/routers/i_router.dart'; | 4 | import 'package:one_poem/routers/i_router.dart'; |
4 | import 'page/poem_complete_page.dart'; | 5 | import 'page/poem_complete_page.dart'; |
5 | import 'page/poem_detail.dart'; | 6 | import 'page/poem_detail.dart'; |
... | @@ -16,6 +17,7 @@ class PoemRouter implements IRouterProvider { | ... | @@ -16,6 +17,7 @@ class PoemRouter implements IRouterProvider { |
16 | static String poemVideoPlayer = '/poem/video/player'; | 17 | static String poemVideoPlayer = '/poem/video/player'; |
17 | static String poemPublish = '/poem/publish'; | 18 | static String poemPublish = '/poem/publish'; |
18 | static String poemCompletePage = '/poem/complete'; | 19 | static String poemCompletePage = '/poem/complete'; |
20 | + static String poemSearchPage = '/poem/search'; | ||
19 | 21 | ||
20 | @override | 22 | @override |
21 | void initRouter(FluroRouter router) { | 23 | void initRouter(FluroRouter router) { |
... | @@ -25,6 +27,7 @@ class PoemRouter implements IRouterProvider { | ... | @@ -25,6 +27,7 @@ class PoemRouter implements IRouterProvider { |
25 | handlerFunc: (_, __) => const PoemPage(), | 27 | handlerFunc: (_, __) => const PoemPage(), |
26 | ), | 28 | ), |
27 | ); | 29 | ); |
30 | + | ||
28 | router.define( | 31 | router.define( |
29 | poemDetailPage, | 32 | poemDetailPage, |
30 | handler: Handler( | 33 | handler: Handler( |
... | @@ -95,5 +98,8 @@ class PoemRouter implements IRouterProvider { | ... | @@ -95,5 +98,8 @@ class PoemRouter implements IRouterProvider { |
95 | }, | 98 | }, |
96 | ), | 99 | ), |
97 | ); | 100 | ); |
101 | + | ||
102 | + router.define(poemSearchPage, | ||
103 | + handler: Handler(handlerFunc: (_, __) => const PoemSearchPage())); | ||
98 | } | 104 | } |
99 | } | 105 | } | ... | ... |
1 | +import 'package:one_poem/mvp/base_page_presenter.dart'; | ||
2 | +import 'package:one_poem/net/dio_utils.dart'; | ||
3 | +import 'package:one_poem/net/http_api.dart'; | ||
4 | +import 'package:one_poem/poem/iview/poem_search_iview.dart'; | ||
5 | +import 'package:one_poem/poem/models/search_entity.dart'; | ||
6 | +import 'package:one_poem/widgets/state_layout.dart'; | ||
7 | + | ||
8 | +class PoemSearchPresenter extends BasePagePresenter<PoemSearchIMvpView> { | ||
9 | + | ||
10 | + Future search(String text, int page, bool isShowDialog) { | ||
11 | + | ||
12 | + final Map<String, String> params = <String, String>{}; | ||
13 | + params['q'] = text; | ||
14 | + params['page'] = page.toString(); | ||
15 | + params['l'] = 'Dart'; | ||
16 | + return requestNetwork<SearchEntity>(Method.get, | ||
17 | + url: HttpApi.search, | ||
18 | + queryParameters: params, | ||
19 | + isShow: isShowDialog, | ||
20 | + onSuccess: (data) { | ||
21 | + if (data != null && data.items != null) { | ||
22 | + /// 一页30条数据,等于30条认为有下一页 | ||
23 | + /// 具体的处理逻辑根据具体的接口情况处理,这部分可以抽离出来 | ||
24 | + view.provider.hasMore = data.items!.length == 30; | ||
25 | + if (page == 1) { | ||
26 | + /// 刷新 | ||
27 | + view.provider.list.clear(); | ||
28 | + if (data.items!.isEmpty) { | ||
29 | + view.provider.setStateType(StateType.order); | ||
30 | + } else { | ||
31 | + view.provider.addAll(data.items!); | ||
32 | + } | ||
33 | + } else { | ||
34 | + view.provider.addAll(data.items!); | ||
35 | + } | ||
36 | + } else { | ||
37 | + /// 加载失败 | ||
38 | + view.provider.hasMore = false; | ||
39 | + view.provider.setStateType(StateType.network); | ||
40 | + } | ||
41 | + }, | ||
42 | + onError: (_, __) { | ||
43 | + /// 加载失败 | ||
44 | + view.provider.hasMore = false; | ||
45 | + view.provider.setStateType(StateType.network); | ||
46 | + } | ||
47 | + ); | ||
48 | + } | ||
49 | + | ||
50 | +} |
lib/poem/provider/base_list_provider.dart
0 → 100644
1 | +import 'package:flutter/material.dart'; | ||
2 | +import 'package:one_poem/widgets/state_layout.dart'; | ||
3 | + | ||
4 | +class BaseListProvider<T> extends ChangeNotifier { | ||
5 | + | ||
6 | + final List<T> _list = <T>[]; | ||
7 | + List<T> get list => _list; | ||
8 | + | ||
9 | + bool hasMore = true; | ||
10 | + | ||
11 | + StateType stateType = StateType.loading; | ||
12 | + | ||
13 | + void setStateType(StateType stateType) { | ||
14 | + this.stateType = stateType; | ||
15 | + notifyListeners(); | ||
16 | + } | ||
17 | + | ||
18 | + void add(T data) { | ||
19 | + _list.add(data); | ||
20 | + notifyListeners(); | ||
21 | + } | ||
22 | + | ||
23 | + void addAll(List<T> data) { | ||
24 | + _list.addAll(data); | ||
25 | + notifyListeners(); | ||
26 | + } | ||
27 | + | ||
28 | + void insert(int i, T data) { | ||
29 | + _list.insert(i, data); | ||
30 | + notifyListeners(); | ||
31 | + } | ||
32 | + | ||
33 | + void insertAll(int i, List<T> data) { | ||
34 | + _list.insertAll(i, data); | ||
35 | + notifyListeners(); | ||
36 | + } | ||
37 | + | ||
38 | + void remove(T data) { | ||
39 | + _list.remove(data); | ||
40 | + notifyListeners(); | ||
41 | + } | ||
42 | + | ||
43 | + void removeAt(int i) { | ||
44 | + _list.removeAt(i); | ||
45 | + notifyListeners(); | ||
46 | + } | ||
47 | + | ||
48 | + void clear() { | ||
49 | + _list.clear(); | ||
50 | + notifyListeners(); | ||
51 | + } | ||
52 | + | ||
53 | + void refresh() { | ||
54 | + notifyListeners(); | ||
55 | + } | ||
56 | +} |
... | @@ -3,19 +3,18 @@ import 'package:flutter/scheduler.dart'; | ... | @@ -3,19 +3,18 @@ import 'package:flutter/scheduler.dart'; |
3 | import 'package:flutter/services.dart'; | 3 | import 'package:flutter/services.dart'; |
4 | import 'package:one_poem/res/resources.dart'; | 4 | import 'package:one_poem/res/resources.dart'; |
5 | import 'package:one_poem/util/theme_utils.dart'; | 5 | import 'package:one_poem/util/theme_utils.dart'; |
6 | - | 6 | +import 'package:one_poem/extension/int_extension.dart'; |
7 | import 'load_image.dart'; | 7 | import 'load_image.dart'; |
8 | import 'my_button.dart'; | 8 | import 'my_button.dart'; |
9 | 9 | ||
10 | /// 搜索页的AppBar | 10 | /// 搜索页的AppBar |
11 | class SearchBar extends StatefulWidget implements PreferredSizeWidget { | 11 | class SearchBar extends StatefulWidget implements PreferredSizeWidget { |
12 | - | ||
13 | const SearchBar({ | 12 | const SearchBar({ |
14 | Key? key, | 13 | Key? key, |
15 | this.hintText = '', | 14 | this.hintText = '', |
16 | this.backImg = 'assets/images/ic_back_black.png', | 15 | this.backImg = 'assets/images/ic_back_black.png', |
17 | this.onPressed, | 16 | this.onPressed, |
18 | - }): super(key: key); | 17 | + }) : super(key: key); |
19 | 18 | ||
20 | final String backImg; | 19 | final String backImg; |
21 | final String hintText; | 20 | final String hintText; |
... | @@ -25,11 +24,10 @@ class SearchBar extends StatefulWidget implements PreferredSizeWidget { | ... | @@ -25,11 +24,10 @@ class SearchBar extends StatefulWidget implements PreferredSizeWidget { |
25 | _SearchBarState createState() => _SearchBarState(); | 24 | _SearchBarState createState() => _SearchBarState(); |
26 | 25 | ||
27 | @override | 26 | @override |
28 | - Size get preferredSize => const Size.fromHeight(48.0); | 27 | + Size get preferredSize => Size.fromHeight(48.px); |
29 | } | 28 | } |
30 | 29 | ||
31 | class _SearchBarState extends State<SearchBar> { | 30 | class _SearchBarState extends State<SearchBar> { |
32 | - | ||
33 | final TextEditingController _controller = TextEditingController(); | 31 | final TextEditingController _controller = TextEditingController(); |
34 | final FocusNode _focus = FocusNode(); | 32 | final FocusNode _focus = FocusNode(); |
35 | 33 | ||
... | @@ -40,34 +38,26 @@ class _SearchBarState extends State<SearchBar> { | ... | @@ -40,34 +38,26 @@ class _SearchBarState extends State<SearchBar> { |
40 | super.dispose(); | 38 | super.dispose(); |
41 | } | 39 | } |
42 | 40 | ||
43 | - // @override | ||
44 | - // void initState() { | ||
45 | - // WidgetsBinding.instance!.addPostFrameCallback((_) async { | ||
46 | - // SystemChannels.textInput.invokeMethod<void>('TextInput.updateConfig', const TextInputConfiguration().toJson()); | ||
47 | - // SystemChannels.textInput.invokeMethod<void>('TextInput.hide'); | ||
48 | - // }); | ||
49 | - // super.initState(); | ||
50 | - // } | ||
51 | - | ||
52 | @override | 41 | @override |
53 | Widget build(BuildContext context) { | 42 | Widget build(BuildContext context) { |
54 | final bool isDark = context.isDark; | 43 | final bool isDark = context.isDark; |
55 | - final Color iconColor = isDark ? Colours.dark_text_gray : Colours.text_gray_c; | 44 | + final Color iconColor = |
56 | - | 45 | + isDark ? Colours.dark_text_gray : Colours.text_gray_c; |
46 | + | ||
57 | final Widget back = Semantics( | 47 | final Widget back = Semantics( |
58 | label: '返回', | 48 | label: '返回', |
59 | child: SizedBox( | 49 | child: SizedBox( |
60 | - width: 48.0, | 50 | + width: 48.px, |
61 | - height: 48.0, | 51 | + height: 48.px, |
62 | child: InkWell( | 52 | child: InkWell( |
63 | onTap: () { | 53 | onTap: () { |
64 | _focus.unfocus(); | 54 | _focus.unfocus(); |
65 | Navigator.maybePop(context); | 55 | Navigator.maybePop(context); |
66 | }, | 56 | }, |
67 | - borderRadius: BorderRadius.circular(24.0), | 57 | + borderRadius: BorderRadius.circular(24.px), |
68 | child: Padding( | 58 | child: Padding( |
69 | key: const Key('search_back'), | 59 | key: const Key('search_back'), |
70 | - padding: const EdgeInsets.all(12.0), | 60 | + padding: EdgeInsets.all(12.px), |
71 | child: Image.asset( | 61 | child: Image.asset( |
72 | widget.backImg, | 62 | widget.backImg, |
73 | color: isDark ? Colours.dark_text : Colours.text, | 63 | color: isDark ? Colours.dark_text : Colours.text, |
... | @@ -77,43 +67,15 @@ class _SearchBarState extends State<SearchBar> { | ... | @@ -77,43 +67,15 @@ class _SearchBarState extends State<SearchBar> { |
77 | ), | 67 | ), |
78 | ); | 68 | ); |
79 | 69 | ||
80 | - /// 使用2.0.0新增CupertinoSearchTextField 实现, 需添加依赖 cupertino_icons: ^1.0.2 | ||
81 | - // final Widget textField1 = Expanded(child: Container( | ||
82 | - // height: 32.0, | ||
83 | - // child: CupertinoSearchTextField( | ||
84 | - // key: const Key('search_text_field'), | ||
85 | - // controller: _controller, | ||
86 | - // focusNode: _focus, | ||
87 | - // placeholder: widget.hintText, | ||
88 | - // placeholderStyle: Theme.of(context).inputDecorationTheme.hintStyle, | ||
89 | - // padding: const EdgeInsetsDirectional.fromSTEB(3.8, 0, 5, 0), | ||
90 | - // prefixInsets: const EdgeInsetsDirectional.fromSTEB(8, 0, 0, 0), | ||
91 | - // suffixInsets: const EdgeInsetsDirectional.fromSTEB(0, 0, 8, 0), | ||
92 | - // style: Theme.of(context).textTheme.subtitle1, | ||
93 | - // itemSize: 16.0, | ||
94 | - // itemColor: iconColor, | ||
95 | - // decoration: BoxDecoration( | ||
96 | - // color: isDark ? Colours.dark_material_bg : Colours.bg_gray, | ||
97 | - // borderRadius: BorderRadius.circular(4.0), | ||
98 | - // ), | ||
99 | - // onSubmitted: (String val) { | ||
100 | - // _focus.unfocus(); | ||
101 | - // // 点击软键盘的动作按钮时的回调 | ||
102 | - // widget.onPressed(val); | ||
103 | - // }, | ||
104 | - // ) | ||
105 | - // )); | ||
106 | - | ||
107 | final Widget textField = Expanded( | 70 | final Widget textField = Expanded( |
108 | child: Container( | 71 | child: Container( |
109 | - height: 32.0, | 72 | + height: 32.px, |
110 | decoration: BoxDecoration( | 73 | decoration: BoxDecoration( |
111 | color: isDark ? Colours.dark_material_bg : Colours.bg_gray, | 74 | color: isDark ? Colours.dark_material_bg : Colours.bg_gray, |
112 | - borderRadius: BorderRadius.circular(4.0), | 75 | + borderRadius: BorderRadius.circular(4.px), |
113 | ), | 76 | ), |
114 | child: TextField( | 77 | child: TextField( |
115 | key: const Key('search_text_field'), | 78 | key: const Key('search_text_field'), |
116 | -// autofocus: true, | ||
117 | controller: _controller, | 79 | controller: _controller, |
118 | focusNode: _focus, | 80 | focusNode: _focus, |
119 | textInputAction: TextInputAction.search, | 81 | textInputAction: TextInputAction.search, |
... | @@ -123,19 +85,24 @@ class _SearchBarState extends State<SearchBar> { | ... | @@ -123,19 +85,24 @@ class _SearchBarState extends State<SearchBar> { |
123 | widget.onPressed?.call(val); | 85 | widget.onPressed?.call(val); |
124 | }, | 86 | }, |
125 | decoration: InputDecoration( | 87 | decoration: InputDecoration( |
126 | - contentPadding: const EdgeInsets.only(left: -8.0, right: -16.0, bottom: 14.0), | 88 | + contentPadding: |
89 | + EdgeInsets.only(left: -8.px, right: -16.px, bottom: 14.px), | ||
127 | border: InputBorder.none, | 90 | border: InputBorder.none, |
128 | icon: Padding( | 91 | icon: Padding( |
129 | - padding: const EdgeInsets.only(top: 8.0, bottom: 8.0, left: 8.0), | 92 | + padding: EdgeInsets.only(top: 8.px, bottom: 8.px, left: 8.px), |
130 | - child: LoadAssetImage('poem/order_search', color: iconColor,), | 93 | + child: LoadAssetImage( |
94 | + 'poem/poem_search', | ||
95 | + color: iconColor, | ||
96 | + ), | ||
131 | ), | 97 | ), |
132 | hintText: widget.hintText, | 98 | hintText: widget.hintText, |
133 | suffixIcon: GestureDetector( | 99 | suffixIcon: GestureDetector( |
134 | child: Semantics( | 100 | child: Semantics( |
135 | label: '清空', | 101 | label: '清空', |
136 | child: Padding( | 102 | child: Padding( |
137 | - padding: const EdgeInsets.only(left: 16.0, top: 8.0, bottom: 8.0), | 103 | + padding: |
138 | - child: LoadAssetImage('poem/order_delete', color: iconColor), | 104 | + EdgeInsets.only(left: 16.px, top: 8.px, bottom: 8.px), |
105 | + child: LoadAssetImage('poem/poem_delete', color: iconColor), | ||
139 | ), | 106 | ), |
140 | ), | 107 | ), |
141 | onTap: () { | 108 | onTap: () { |
... | @@ -149,20 +116,20 @@ class _SearchBarState extends State<SearchBar> { | ... | @@ -149,20 +116,20 @@ class _SearchBarState extends State<SearchBar> { |
149 | ), | 116 | ), |
150 | ), | 117 | ), |
151 | ); | 118 | ); |
152 | - | 119 | + |
153 | final Widget search = MyButton( | 120 | final Widget search = MyButton( |
154 | - minHeight: 32.0, | 121 | + minHeight: 32.px, |
155 | - minWidth: 44.0, | 122 | + minWidth: 44.px, |
156 | fontSize: Dimens.font_sp14, | 123 | fontSize: Dimens.font_sp14, |
157 | - radius: 4.0, | 124 | + radius: 4.px, |
158 | - padding: const EdgeInsets.symmetric(horizontal: 8.0), | 125 | + padding: EdgeInsets.symmetric(horizontal: 8.px), |
159 | text: '搜索', | 126 | text: '搜索', |
160 | - onPressed:() { | 127 | + onPressed: () { |
161 | _focus.unfocus(); | 128 | _focus.unfocus(); |
162 | widget.onPressed?.call(_controller.text); | 129 | widget.onPressed?.call(_controller.text); |
163 | }, | 130 | }, |
164 | ); | 131 | ); |
165 | - | 132 | + |
166 | return AnnotatedRegion<SystemUiOverlayStyle>( | 133 | return AnnotatedRegion<SystemUiOverlayStyle>( |
167 | value: isDark ? SystemUiOverlayStyle.light : SystemUiOverlayStyle.dark, | 134 | value: isDark ? SystemUiOverlayStyle.light : SystemUiOverlayStyle.dark, |
168 | child: Material( | 135 | child: Material( | ... | ... |
-
Please register or login to post a comment