我搜索应用程序栏。我按标题(子字符串)搜索数据并返回项目。我有Ui,bloc,服务类。我不知道如何将'$ searchQuery'传递给块接收器。我返回流列表(在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();
}
}
答案 0 :(得分: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