feat:增加博客搜索功能
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
import 'package:blog_app/apis/blog.dart';
|
||||
import 'package:blog_app/layout/drawer.dart';
|
||||
import 'package:blog_app/layout/menu.dart';
|
||||
import 'package:blog_app/models/blog.dart';
|
||||
import 'package:blog_app/widget/search.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class HomePage extends StatefulWidget {
|
||||
@@ -11,6 +14,12 @@ class _HomePageState extends State<HomePage> {
|
||||
int _currentPageIndex = 0;
|
||||
late PageController _pageController;
|
||||
|
||||
bool _isSearching = false;
|
||||
final TextEditingController _searchController = TextEditingController();
|
||||
final FocusNode _searchFocusNode = FocusNode();
|
||||
List<BlogSearch> _searchResults = [];
|
||||
String _searchQuery = '';
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
@@ -20,53 +29,145 @@ class _HomePageState extends State<HomePage> {
|
||||
@override
|
||||
void dispose() {
|
||||
_pageController.dispose();
|
||||
_searchController.dispose();
|
||||
_searchFocusNode.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _startSearch() {
|
||||
setState(() {
|
||||
_isSearching = true;
|
||||
});
|
||||
Future.delayed(Duration(milliseconds: 100), () {
|
||||
_searchFocusNode.requestFocus();
|
||||
});
|
||||
}
|
||||
|
||||
void _exitSearch() {
|
||||
setState(() {
|
||||
_isSearching = false;
|
||||
_searchController.clear();
|
||||
_searchResults.clear();
|
||||
_searchQuery = '';
|
||||
});
|
||||
FocusScope.of(context).unfocus();
|
||||
}
|
||||
|
||||
void _clearSearch() {
|
||||
_searchController.clear();
|
||||
setState(() {
|
||||
_searchResults.clear();
|
||||
_searchQuery = '';
|
||||
});
|
||||
}
|
||||
|
||||
void _onSearchTextChanged(String query) async {
|
||||
setState(() {
|
||||
_searchQuery = query;
|
||||
});
|
||||
|
||||
if (query.isEmpty) {
|
||||
setState(() {
|
||||
_searchResults.clear();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
final result = await searchBlog(query);
|
||||
|
||||
setState(() {
|
||||
_searchResults = result;
|
||||
});
|
||||
}
|
||||
|
||||
Widget _buildLeading() {
|
||||
if (_isSearching) {
|
||||
return IconButton(
|
||||
icon: Icon(Icons.arrow_back, color: Colors.white),
|
||||
onPressed: _exitSearch,
|
||||
);
|
||||
}
|
||||
|
||||
return Builder(
|
||||
builder:
|
||||
(context) => IconButton(
|
||||
icon: Icon(Icons.menu, color: Colors.white),
|
||||
onPressed: () => Scaffold.of(context).openDrawer(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildAppBarTitle() {
|
||||
if (_isSearching) {
|
||||
return buildSearchField(
|
||||
controller: _searchController,
|
||||
focusNode: _searchFocusNode,
|
||||
onChanged: _onSearchTextChanged,
|
||||
);
|
||||
}
|
||||
|
||||
return Text(
|
||||
pages[_currentPageIndex].title,
|
||||
style: TextStyle(color: Colors.white),
|
||||
);
|
||||
}
|
||||
|
||||
List<Widget> _buildAppBarActions() {
|
||||
if (_isSearching) {
|
||||
return [
|
||||
if (_searchController.text.isNotEmpty)
|
||||
IconButton(
|
||||
icon: Icon(Icons.clear, color: Colors.white),
|
||||
onPressed: _clearSearch,
|
||||
),
|
||||
];
|
||||
} else {
|
||||
return [
|
||||
IconButton(
|
||||
icon: Icon(Icons.search, color: Colors.white),
|
||||
onPressed: _startSearch,
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildPageView() {
|
||||
return _isSearching ? _buildSearchResults() : _buildNormalPageView();
|
||||
}
|
||||
|
||||
Widget _buildNormalPageView() {
|
||||
return PageView(
|
||||
controller: _pageController,
|
||||
onPageChanged: (index) {
|
||||
setState(() {
|
||||
_currentPageIndex = index;
|
||||
if (_isSearching) {
|
||||
_exitSearch();
|
||||
}
|
||||
});
|
||||
},
|
||||
children: pageWidgets,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(
|
||||
pages[_currentPageIndex].title,
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||
elevation: 0,
|
||||
leading: Builder(
|
||||
builder:
|
||||
(context) => IconButton(
|
||||
icon: Icon(Icons.menu, color: Colors.white),
|
||||
onPressed: () => Scaffold.of(context).openDrawer(),
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: Icon(Icons.search, color: Colors.white),
|
||||
onPressed: () {},
|
||||
),
|
||||
],
|
||||
),
|
||||
drawer: _buildDrawer(),
|
||||
body: Padding(padding: EdgeInsets.all(10), child: _buildPageView()),
|
||||
);
|
||||
Widget _buildSearchResults() {
|
||||
if (_searchQuery.isEmpty) {
|
||||
return buildSearchQueryEmpty();
|
||||
}
|
||||
|
||||
if (_searchResults.isEmpty) {
|
||||
return buildSearchResultEmpty(_searchQuery);
|
||||
}
|
||||
|
||||
return buildSearchResultList(context, _searchResults);
|
||||
}
|
||||
|
||||
void onTapDrawerItem(int index) {
|
||||
setState(() {
|
||||
_currentPageIndex = index;
|
||||
if (_isSearching) {
|
||||
_exitSearch();
|
||||
}
|
||||
});
|
||||
_pageController.animateToPage(
|
||||
index,
|
||||
@@ -99,13 +200,26 @@ class _HomePageState extends State<HomePage> {
|
||||
color: Colors.white,
|
||||
child: Column(
|
||||
children: [
|
||||
// 抽屉头部
|
||||
buildDrawerHeader(context),
|
||||
// 菜单项列表
|
||||
Expanded(child: _buildDrawerBody()),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: _buildAppBarTitle(),
|
||||
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||
elevation: 0,
|
||||
leading: _buildLeading(),
|
||||
actions: _buildAppBarActions(),
|
||||
),
|
||||
drawer: _buildDrawer(),
|
||||
body: Padding(padding: EdgeInsets.all(10), child: _buildPageView()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,10 +38,10 @@ class TimelinePageState extends State<TimelinePage> {
|
||||
return Indicator.dot(size: 12.0, color: colors.primary);
|
||||
},
|
||||
contentsBuilder: (context, index) {
|
||||
return buildBlogListItem(
|
||||
context,
|
||||
provider.blogList[index],
|
||||
index + 1,
|
||||
final blog = provider.blogList[index];
|
||||
return InkWell(
|
||||
onTap: () => navigatorToBlogDetail(context, blog.id),
|
||||
child: buildBlogListItem(context, blog, index + 1),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user