I'm new to flutter and I'm building a program that puts text in Textfield and searches for it in algolia.
It works fine up to the point where I enter text and algolia processes it, but it doesn't return any results and nothing is displayed on the screen.
Is it because I am using Stream Builder? (Would Futurebuilder be better?) I'm not sure, so I would appreciate it if someone could help me.
import 'dart:async';
import 'package:algolia/algolia.dart';
import 'package:flutter/material.dart';
import '../../main.dart';
class Application {
static final Algolia algolia = Algolia.init(
applicationId: '78CZVABC2W',
apiKey: 'c2377e7faad9a408d5867b849f25fae4',
);
}
class Search extends StatefulWidget {
@override
_SearchState createState() => _SearchState();
}
class _SearchState extends State<Search> {
StreamController<List<AlgoliaObjectSnapshot>> searchController = StreamController();
Future searchFireStore(word) async {
Algolia algolia = Application.algolia;
AlgoliaQuery query = algolia.instance.index("rigaku_index").query(word);
AlgoliaQuerySnapshot snap = await query.getObjects();
List<AlgoliaObjectSnapshot> hits = snap.hits;
searchController.add(hits);
print(hits.length);
}
final _algilia = Application.algolia;
@override
void dispose() {
searchController.close();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: SearchItems(),
backgroundColor: Colors.white,
),
body: Column(
children: <Widget>[
Expanded(
child: StreamBuilder(
stream: searchController.stream,
builder: (context,AsyncSnapshot snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return Text((snapshot.data[index].data['zyugyoumei']));
},
);
} else {
return Center(
child: Text("No Data"),
);
}
},
),
)
],
),
);
}
}
class SearchItems extends StatefulWidget {
@override
_SearchItemsState createState() => _SearchItemsState();
}
class _SearchItemsState extends State<SearchItems> {
String searchWord = "";
@override
Widget build(BuildContext context) {
return TextField(
decoration: const InputDecoration(
hintText: '検索',
),
controller: TextEditingController(text: searchWord),
onChanged: (String text) {
setState(() {
searchWord = text;
});
},
onSubmitted: (searchWord) =>_SearchState().searchFireStore(searchWord)); }
}
There are no error messages, but the snapshot in the StreamBuilder is null. The [hits] in Future is displayed correctly.
There are the following problems solving those should get you your desired result:
searchController
is not typed properly as
per your data structure type will emit something likeStreamController<List<AlgoliaObjectSnapshot>> searchController = StreamController();
In this way we know what we expect to return after each hit.
searchWord
properly is should be set with using setState
like following:onChanged: (String text) {
setState(() {
searchWord = text;
});
},
searchFireStore
incorrectly instead of _SearchState().searchFireStore(searchWord)
you should create instance variable _SearchState _searchState;
and calling it like _searchState.searchFireStore(searchWord);
where _searchState is an instance variable of type _SearchState that we have just created.searchController
properly as follows:@override
void dispose() {
searchController.close();
super.dispose();
}
And for your using StreamBuilder
vs FutureBuilder
debate it depends on your use case like StreamBuilder
is more appropriate for cases where you expect the data to be updated frequently, while FutureBuilder
is more appropriate for cases where the data is fetched once and doesn't change often.
In your case, since you're using a stream to receive search results, so in this case StreamBuilder
seems correct choice.
For more information go through StreamBuilder vs FutureBuilder and Algolia ListView Search
EDIT : I have managed to recreate all the algolia with flutter as follows:
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'firebase_options.dart';
import 'package:algolia/algolia.dart';
import 'dart:async';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Algolia Search',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Search(),
);
}
}
class Search extends StatefulWidget {
@override
_SearchState createState() => _SearchState();
}
class _SearchState extends State<Search> {
StreamController<List<AlgoliaObjectSnapshot>> searchController =
StreamController();
List<AlgoliaObjectSnapshot> _results = [];
final Algolia algolia = const Algolia.init(
applicationId: '<app_id>',
apiKey: '<api_key>',
);
Future<List<AlgoliaObjectSnapshot>> searchAlgolia(String query) async {
AlgoliaQuery algoliaQuery = algolia.instance.index('demo').query(query);
AlgoliaQuerySnapshot snap = await algoliaQuery.getObjects();
return snap.hits;
}
void _performSearch(String query) async {
List<AlgoliaObjectSnapshot> results = await searchAlgolia(query);
setState(() {
_results = results;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Algolia Search'),
),
body: Column(
children: <Widget>[
TextField(
onChanged: (query) => _performSearch(query),
decoration: const InputDecoration(
hintText: 'Search...',
prefixIcon: Icon(Icons.search),
),
),
Expanded(
child: ListView.builder(
itemCount: _results.length,
itemBuilder: (context, index) {
AlgoliaObjectSnapshot result = _results[index];
return ListTile(
title: Text(result.data['firstname']),
subtitle: Text(result.data['lastname']),
);
},
),
),
],
),
);
}
}
So the only thing need to be edited here is app_id api_key and off course the index name and it's values.