颤振搜索栏与带有firestore的块
Posted
技术标签:
【中文标题】颤振搜索栏与带有firestore的块【英文标题】:flutter search bar with bloc with firestore 【发布时间】:2020-07-23 17:27:06 【问题描述】:我搜索应用程序栏。我按标题(子字符串)搜索数据并返回项目。我有 Ui、bloc、服务类。我不知道如何将“$searchQuery”传递到 bloc 接收器。我返回流列表(在Ui),但首先我需要按标题(项目)过滤我的列表并放入接收器。你能给我一些建议或例子吗?我应该修复我的 bloc 类吗?
class Search extends StatefulWidget
@override
_Search createState() => _Search();
class _Search extends State<Search>
static final GlobalKey<ScaffoldState> scaffoldKey =
new GlobalKey<ScaffoldState>();
TextEditingController _searchQuery;
bool _isSearching = false;
String searchQuery = "Search query";
@override
void initState()
super.initState();
_searchQuery = new TextEditingController();
void _startSearch()
print("open search box");
ModalRoute.of(context)
.addLocalHistoryEntry(new LocalHistoryEntry(onRemove: _stopSearching));
setState(()
_isSearching = true;
);
void _stopSearching()
_clearSearchQuery();
setState(()
_isSearching = false;
);
void _clearSearchQuery()
print("close search box");
setState(()
_searchQuery.clear();
updateSearchQuery("Search query");
);
Widget _buildTitle(BuildContext context)
var horizontalTitleAlignment =
Platform.isios ? CrossAxisAlignment.center : CrossAxisAlignment.start;
return new InkWell(
onTap: () => scaffoldKey.currentState.openDrawer(),
child: new Padding(
padding: const EdgeInsets.symmetric(horizontal: 12.0),
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: horizontalTitleAlignment,
children: <Widget>[
const Text('Seach box'),
],
),
),
);
Widget _buildSearchField()
return new TextField(
controller: _searchQuery,
autofocus: true,
decoration: const InputDecoration(
hintText: 'Search...',
border: InputBorder.none,
hintStyle: const TextStyle(color: Colors.white30),
),
style: const TextStyle(color: Colors.white, fontSize: 16.0),
onChanged: updateSearchQuery,
);
void updateSearchQuery(String newQuery)
setState(()
searchQuery = newQuery;
);
print("search query " + newQuery);
List<Widget> _buildActions()
if (_isSearching)
return <Widget>[
new IconButton(
icon: const Icon(Icons.clear),
onPressed: ()
if (_searchQuery == null || _searchQuery.text.isEmpty)
Navigator.pop(context);
return;
_clearSearchQuery();
,
),
];
return <Widget>[
new IconButton(
icon: const Icon(Icons.search),
onPressed: _startSearch,
),
];
@override
Widget build(BuildContext context)
return SafeArea(
child: Scaffold(
key: scaffoldKey,
appBar: new AppBar(
leading: _isSearching ? const BackButton() : null,
title: _isSearching ? _buildSearchField() : _buildTitle(context),
actions: _buildActions(),
),
body: new Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Text(
'$searchQuery',
style: Theme.of(context).textTheme.display1,
)
],
),
),
));
class MovieService
static final String _baseUrl = 'xxxx';
final CollectionReference _db;
MovieService() : _db = Firestore.instance.collection(_baseUrl);
Future<List<MovieEntity>> searchByString(String stringSearch) async
final CollectionReference _dbs = Firestore.instance.collection(_baseUrl);
QuerySnapshot query =
await _dbs.where("Title", isEqualTo: stringSearch.substring(0, 1).toUpperCase()).getDocuments();
List<MovieEntity> products = query.documents
.map((doc) => MovieEntity.fromSnapshotJson(doc))
.toList();
return products;
class MovieBloc extends BlocBase
String nameFilmSearchQuery;
final MovieService _productService;
MovieBloc(this._productService)
_loadMovies();
final BehaviorSubject<List<MovieEntity>> _controllerSearch =
new BehaviorSubject<List<MovieEntity>>.seeded(List<MovieEntity>());
Observable<List<MovieEntity>> get listMoviesFluxSearch =>
_controllerSearch.stream;
Sink<List<MovieEntity>> get listMoviesEventSearch => _controllerSearch.sink;
_loadMovies() async
listMoviesEventSearch.add(await _productService.searchByString(nameFilmSearchQuery));// i should pass '$searchQuery' here from Ui
@override
void dispose()
_controllerSearch.close();
super.dispose();
【问题讨论】:
【参考方案1】:此功能的一种可能实现,包括相对于更改状态和事件的主 BLoC 使用的 SearchDelegate。 在这种情况下,所需的资源是一个 City,因此它实现了一个扩展 SearchDelegate 的“SearchCity”类。 重写方法 buildResults 和 BlocBuilder,它还添加了 SearchCity 事件和一些 SearchCity 状态,以便轻松管理与搜索操作本身相关的所有可能操作。
class CitySearchEvent
final String query;
const CitySearchEvent(this.query);
@override
String toString() => 'CitySearchEvent query: $query ';
class CitySearchState
final bool isLoading;
final List<City> cities;
final bool hasError;
const CitySearchState(this.isLoading, this.cities, this.hasError);
factory CitySearchState.initial()
return CitySearchState(
cities: [],
isLoading: false,
hasError: false,
);
factory CitySearchState.loading()
return CitySearchState(
cities: [],
isLoading: true,
hasError: false,
);
factory CitySearchState.success(List<City> cities)
return CitySearchState(
cities: cities,
isLoading: false,
hasError: false,
);
factory CitySearchState.error()
return CitySearchState(
cities: [],
isLoading: false,
hasError: true,
);
@override
String toString() =>
'CitySearchState cities: $cities.toString(), isLoading: $isLoading, hasError: $hasError ';
这是添加到主要 BLoC 实现的编辑,它使用以前创建的状态和事件来为操作构建更好的业务逻辑。
class CityBloc extends Bloc<CitySearchEvent, CitySearchState>
@override
CitySearchState get initialState => CitySearchState.initial();
@override
void onTransition(Transition<CitySearchEvent, CitySearchState> transition)
print(transition.toString());
@override
Stream<CitySearchState> mapEventToState(CitySearchEvent event) async*
yield CitySearchState.loading();
try
List<City> cities = await _getSearchResults(event.query);
yield CitySearchState.success(cities);
catch (_)
yield CitySearchState.error();
Future<List<City>> _getSearchResults(String query) async
// Simulating network latency
await Future.delayed(Duration(seconds: 1));
return [City('Chicago'), City('Los Angeles')];
这是一个包含 UI 和搜索方法的单文件实现的完整示例。
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:bloc/bloc.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget
@override
Widget build(BuildContext context)
return MaterialApp(
title: 'Flutter Demo',
home: BlocProvider(
create: (_) => CityBloc(),
child: MyHomePage(),
));
class MyHomePage extends StatelessWidget
@override
Widget build(BuildContext context)
return Scaffold(
appBar: AppBar(
title: Text('Search Delegate'),
),
body: Container(
child: Center(
child: RaisedButton(
child: Text('Show search'),
onPressed: () async
City selected = await showSearch<City>(
context: context,
delegate: CitySearch(BlocProvider.of<CityBloc>(context)),
);
print(selected);
,
),
),
),
);
class City
final String name;
const City(this.name);
@override
String toString() => 'City name: $name ';
class CitySearch extends SearchDelegate<City>
final Bloc<CitySearchEvent, CitySearchState> cityBloc;
CitySearch(this.cityBloc);
@override
List<Widget> buildActions(BuildContext context) => null;
@override
Widget buildLeading(BuildContext context)
return IconButton(
icon: BackButtonIcon(),
onPressed: ()
close(context, null);
,
);
@override
Widget buildResults(BuildContext context)
cityBloc.add(CitySearchEvent(query));
return BlocBuilder(
bloc: cityBloc,
builder: (BuildContext context, CitySearchState state)
if (state.isLoading)
return Center(
child: CircularProgressIndicator(),
);
if (state.hasError)
return Container(
child: Text('Error'),
);
return ListView.builder(
itemBuilder: (context, index)
return ListTile(
leading: Icon(Icons.location_city),
title: Text(state.cities[index].name),
onTap: () => close(context, state.cities[index]),
);
,
itemCount: state.cities.length,
);
,
);
@override
Widget buildSuggestions(BuildContext context) => Container();
class CitySearchEvent
final String query;
const CitySearchEvent(this.query);
@override
String toString() => 'CitySearchEvent query: $query ';
class CitySearchState
final bool isLoading;
final List<City> cities;
final bool hasError;
const CitySearchState(this.isLoading, this.cities, this.hasError);
factory CitySearchState.initial()
return CitySearchState(
cities: [],
isLoading: false,
hasError: false,
);
factory CitySearchState.loading()
return CitySearchState(
cities: [],
isLoading: true,
hasError: false,
);
factory CitySearchState.success(List<City> cities)
return CitySearchState(
cities: cities,
isLoading: false,
hasError: false,
);
factory CitySearchState.error()
return CitySearchState(
cities: [],
isLoading: false,
hasError: true,
);
@override
String toString() =>
'CitySearchState cities: $cities.toString(), isLoading: $isLoading, hasError: $hasError ';
class CityBloc extends Bloc<CitySearchEvent, CitySearchState>
@override
CitySearchState get initialState => CitySearchState.initial();
@override
void onTransition(Transition<CitySearchEvent, CitySearchState> transition)
print(transition.toString());
@override
Stream<CitySearchState> mapEventToState(CitySearchEvent event) async*
yield CitySearchState.loading();
try
List<City> cities = await _getSearchResults(event.query);
yield CitySearchState.success(cities);
catch (_)
yield CitySearchState.error();
Future<List<City>> _getSearchResults(String query) async
// Simulating network latency
await Future.delayed(Duration(seconds: 1));
return [City('Chicago'), City('Los Angeles')];
可以在这个 Github Gist 链接上找到上述代码的参考 https://gist.github.com/felangel/11769ab10fbc4076076299106f48fc95
【讨论】:
以上是关于颤振搜索栏与带有firestore的块的主要内容,如果未能解决你的问题,请参考以下文章