I want to keep the selected value visible after the users click the connect button. How do I keep the selected value in this dropdown still appear when the user has clicked the connect button? This is the preview of my application:
And this is my code:
StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection('servers')
.snapshots(includeMetadataChanges: true),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return Text('Something went wrong');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return Text("Loading");
}
return Container(
child: DropdownSearch<String>(
dropdownDecoratorProps: DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration(
labelText: "Server",
labelStyle: TextStyle(color: Color(0xFFB4F4C8)),
hintText: "Select a server",
hintStyle: TextStyle(color: Color(0xFFB4F4C8)),
icon: Icon(
Icons.map_outlined,
color: Color(0xFFB4F4C8),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Color(0xFFB4F4C8)),
borderRadius: BorderRadius.circular(10.0),
),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Color(0xFFB4F4C8)),
borderRadius: BorderRadius.circular(8.0)),
),
),
dropdownBuilder: ((context, selectedItem) {
Icon(Icons.arrow_drop_down_circle_outlined,
color: Color(0xFFB4F4C8),
);
return Text(selectedItem ?? "",
style: TextStyle(color: Color(0xFFfcfcfc)),
);
}),
items: snapshot.data!.docs.map((DocumentSnapshot document) {
Map<String, dynamic> data = document.data()! as Map<String, dynamic>;
return data["address"];
})
.toList()
.cast<String>(),
onChanged: (var data) {
dataAddress = data;
}
),
);
},
),
Thank you in advance for any help.
It looks like you are not using showSearchBox: true on DropdownSearch widget.
Although docs shows that we can use dropdownDecoratorProps: DropDownDecoratorProps but in your case it seems it is not needed as there aren't many dropdown search fields.
Hence I have used it without those Props in my below code.
I have tried below code at my end i am able to list drop down. Here is the screenshot of my app :
main.dart with DropDownWidget :
class DropDownWidget extends StatefulWidget {
const DropDownWidget({Key? key}) : super(key: key);
#override
State<DropDownWidget> createState() => _DropDownWidgetState();
}
class _DropDownWidgetState extends State<DropDownWidget> {
String? dataAddress;
final snappy = FirebaseFirestore.instance
.collection('users')
.snapshots(includeMetadataChanges: true);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('AppBar Demo')),
body: StreamBuilder<QuerySnapshot>(
stream: snappy,
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return const Text('Something went wrong');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return const Text("Loading");
}
return DropdownSearch<String>(
dropdownSearchDecoration: const InputDecoration(
labelText: "Server",
hintText: "Select a server",
),
dropdownBuilder: ((context, selectedItem) {
const Icon(
Icons.arrow_drop_down_circle_outlined,
);
return Text(selectedItem ?? "");
}),
items: snapshot.data!.docs
.map((DocumentSnapshot document) {
Map<String, dynamic> data =
document.data()! as Map<String, dynamic>;
return data["address"];
})
.toList()
.cast<String>(),
onChanged: (var data) {
dataAddress = data;
});
},
),
);
}
}
Related
I want to add search functionality from Api. It is a backend search, so I get data continuous when clicked on on-changed function. I use future provider to get data. Please tell how can I achieve that.
Here Is my design what I want to do. Ui Image Demo
Also Here Is my code demo
`
class SearchPage extends StatelessWidget {
const SearchPage({super.key});
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => SearchProvider()),
],
child: Consumer<SearchProvider>(
builder: (context, value, child) => Scaffold(
appBar: BuildSearchAppBar(),
body: Body()
),
),
);
}
}
class BuildSearchAppBar extends StatelessWidget with PreferredSizeWidget {
BuildSearchAppBar({super.key});
#override
Size get preferredSize => const Size.fromHeight(kToolbarHeight);
#override
Widget build(BuildContext context) {
SearchProvider provider = Provider.of<SearchProvider>(context);
return AppBar(
title: TextField(
controller: provider.textEditingController,
decoration: InputDecoration(
alignLabelWithHint: true,
floatingLabelBehavior: FloatingLabelBehavior.never,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(6.r),
),
constraints: BoxConstraints.tight(Size(1.sw, 40.h)),
labelText: "Search",
prefixIcon: Icon(
Icons.search,
size: 20.r,
),
labelStyle: TextStyle(fontSize: 15.sp, fontStyle: FontStyle.italic),
),
onChanged: (value) {
provider.showProductSuggetion();
},
),
actions: [
IconButton(
onPressed: () {
provider.textEditingController.clear();
},
icon: const Icon(Icons.clear),
),
],
);
}
}
class SearchProvider with ChangeNotifier {
bool isClicked = false;
final TextEditingController textEditingController = TextEditingController();
void showProductSuggetion() {
isClicked = true;
FutureProvider(
create: (_) => searchSuggestionService(textEditingController.text),
initialData: SearchSuggetionModel());
notifyListeners();
}
Future<SearchSuggetionModel> searchSuggestionService(String keyword) async {
Map<String, Map<String, Object>> singleProductVariable = {
"productPrams": {"search": keyword, "visibility": true, "approved": true}
};
QueryResult queryResult = await qlclient.query(
QueryOptions(
document: gql(QueryDocument.searchSuggestion),
variables: singleProductVariable),
);
var data = queryResult.data as Map<String, dynamic>;
print(data);
var body = SearchSuggetionModel.fromJson(data);
notifyListeners();
return body;
}
}
I want to implement backend search with continues data fetching using provider.
I am a little bit confused on why a GraphQL query returning a list of items does not get updated. Say I have a list of three items, then I add another item and rebuild the widget. The widget still only shows the first three items, even though I can see that new item has been created in the database. I am unsure if this is a cache problem. I have tried to fetch data from the network only, but that does not work either.
The client used in the GraphQLProvider is instantiated like this:
Future<ValueNotifier<GraphQLClient>> _getClient() async {
final HttpLink httpLink = HttpLink(
Constants.apiURL,
defaultHeaders: {
'X-Parse-Application-Id': Constants.kParseApplicationId,
'X-Parse-Client-Key': Constants.kParseClientKey,
},
);
// initialize Hive and wrap the default box in a HiveStore
Directory directory = await pathProvider.getApplicationDocumentsDirectory();
final store = await HiveStore.open(path: directory.path);
return ValueNotifier(
GraphQLClient(
cache: GraphQLCache(store: store),
link: httpLink,
),
);
}
And the page looks like this. When a new forum post is created, setstate() is called and the widget rebuilds. However, the line List<dynamic> forumEntries = result.data?["getForumEntries"]; still returns the old list of data without the new entry. I have the same problem in a few other places as well.
class FeedWidget extends StatefulWidget {
const FeedWidget({Key? key}) : super(key: key);
#override
State<FeedWidget> createState() => _FeedWidgetState();
}
class _FeedWidgetState extends State<FeedWidget> {
final TextEditingController controller = TextEditingController();
void _createForumPost() async {
Map<String, dynamic> inputVariables = {
"questionText": controller.text,
};
GraphQLClient client = GraphQLProvider.of(context).value;
await client.query(
QueryOptions(
document: gql(GraphQLQueries.createForumPost),
variables: inputVariables,
),
);
setState(() {
controller.text = "";
});
}
#override
Widget build(BuildContext context) {
return Query(
options: QueryOptions(
fetchPolicy: FetchPolicy.networkOnly,
document: gql(GraphQLQueries.getForumEntries),
),
builder: (QueryResult result,
{VoidCallback? refetch, FetchMore? fetchMore}) {
if (result.hasException) {
return Text(result.exception.toString());
}
if (result.isLoading) {
return const Center(child: CircularProgressIndicator());
}
List<dynamic> forumEntries = result.data?["getForumEntries"];
return Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Row(
children: [
Expanded(
child: TextField(
controller: controller,
keyboardType: TextInputType.multiline,
maxLines: null,
autocorrect: false,
decoration: InputDecoration(
fillColor: Theme.of(context).colorScheme.surface,
labelText: "Content",
filled: true,
border: InputBorder.none,
),
),
),
const Padding(padding: EdgeInsets.symmetric(horizontal: 3)),
CustomIconButton(
padding: EdgeInsets.zero,
icon: const Icon(Icons.send),
onPressed: () => _createForumPost(),
),
],
),
const Padding(padding: EdgeInsets.only(bottom: 10)),
Expanded(
child: ListView.builder(
shrinkWrap: true,
itemCount: forumEntries.length,
itemBuilder: (BuildContext context, int index) {
Map<String, dynamic> entry = forumEntries[index];
return ForumEntryWidget(entry);
},
),
),
],
);
},
);
}
}
I was stuck on this and solved it easily using refetch functions returned by Query. As your CustomIconButton are inside the Query, you can replace _createForumPost() with refetch(). In my case I use the RefreshIndicator creating a function Future<void> which calls the refetch method.
#override
Widget build(BuildContext context) {
// resp = ListView();
return Scaffold(
body: Query(
options: QueryOptions(
document: gql(documentQ),
fetchPolicy: FetchPolicy.noCache,
cacheRereadPolicy: CacheRereadPolicy.ignoreAll,
optimisticResult: true),
builder: (QueryResult result,
{Refetch? refetch, FetchMore? fetchMore}) {
if (result.isLoading) {
return const Center(child: CircularProgressIndicator());
}
if (result.data == null) {
return const Center(child: Text('Nenhum tour por perto'));
}
return _toursView(result, refetch);
},
),
);
}
RefreshIndicator _toursView(QueryResult result, refetch) {
// print(result.data?['tourAll']);
Future<void> refresh(refetch) {
return refetch();
}
final toursList = result.data?['tourAll'];
return RefreshIndicator(
onRefresh: () => refresh(refetch),
child: ...
I'm implementing a search functionality in the app. I'm using firebase where clause to filter the data
the filtered data appears for a second and it then disappeared immediately
here is my code
String searchName = "";
TextFormField(
onChanged: (value){
setState((){
searchName = value;
});
},
decoration: InputDecoration(
hintText: 'SEARCH',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(25.0),
borderSide: BorderSide.none
)
),
),
StreamBuilder(
stream: searchName != "" ? FirebaseFirestore.instance.collectionGroup('user_offers').where("fieldName", isEqualTo: searchName).snapshots() : FirebaseFirestore.instance.collectionGroup('user_offers').snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot<Map<String, dynamic>>> snapshot) {
if(snapshot.hasData){
return ListView.builder() // returning a list
}
return const Center(child: CircularProgressIndicator(color: Colors.white));
},
),
What am I doing wrong? The data appears for a second and it then disappears instantly
You should have to use two conditions 1st for the state (e.g. ConnectionState, waiting, done) and 2nd for null check. whenever you use FIREBASE WHERE you should always use null check. (e.g. if statement and then must else).
It's true snapshot parament have data. But snapshot.data!.docs have with null. You must check it (snapshot.data!.docs) with if else statement.
disappears instantly because you haven't checked snapshot.data!.docs with if else statement either empty or not empty.
I hope it could help.
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:towermarket/models/order.dart';
import '../order_details_screen.dart';
class InProgressOrders extends StatelessWidget {
const InProgressOrders({
Key? key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot<Map<String, dynamic>>>(
stream: FirebaseFirestore.instance
.collection("orders")
.where("completed", isEqualTo: false)
.snapshots(),
builder:
(_, AsyncSnapshot<QuerySnapshot<Map<String, dynamic>>> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
case ConnectionState.waiting:
return const Center(
child: CircularProgressIndicator(),
);
case ConnectionState.active:
case ConnectionState.done:
final List<Order> orders =
snapshot.data!.docs.map((e) => Order.fromSnapshot(e)).toList();
if (orders.isNotEmpty) {
return ListView.builder(
itemCount: orders.length,
itemBuilder: (_, index) {
return ListTile(
onTap: () {
Navigator.push(context, OrderDetailsScreen.route());
},
leading: Text("${index + 1}"),
title: Text(orders[index].reference!),
subtitle: Text(orders[index].address),
trailing: Text("PKR ${orders[index].total}"),
);
},
);
} else {
return const Center(
child: Text("No, In Progress Order"),
);
}
default:
return const Center(
child: Text("Somethign went wrong"),
);
}
},
);
}
}
The code below is what I am trying now. The page works does everything I need but now I need this database reference to use the loanuid, clientuid, and companyName to get to the right directory.
StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection('prosperitybank')
.doc('OHViYK8Zz6XfKGJsSXRL')
.collection('Project Information')
.snapshots()```
I need it from my collection.(userCreationRef).doc(loggedinuid) as shown in the picture. I can not figure out how to do this without the stream builders interfering any help would be greatly appreciated. I have tried to using this to help but it did not How can you nest StreamBuilders in Flutter?. I also tried looking at the documentation here https://firebase.flutter.dev/docs/firestore/usage/.
Picture of Document I need data fields from
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:photoloanupdated/screens/mains/viewproperties.dart';
import 'package:provider/provider.dart';
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
final FirebaseAuth auth = FirebaseAuth.instance;
final user = auth.currentUser;
final uid = user?.uid;
var users = FirebaseFirestore.instance.collection('userCreationRequests');
var companyname = "";
return Scaffold(
appBar: AppBar(
title: Text(companyname),
),
body:
FutureBuilder<DocumentSnapshot>(
future: users.doc(uid).get(),
builder:
(BuildContext context, AsyncSnapshot<DocumentSnapshot> snapshot) {
if (snapshot.hasError) {
return Text("Something went wrong");
}
if (snapshot.hasData && !snapshot.data!.exists) {
return Text("Document does not exist");
}
if (snapshot.connectionState == ConnectionState.done) {
Map<String, dynamic> data =
snapshot.data!.data() as Map<String, dynamic>;
return Text("Full Name: ${data['companyName']} ${data['last_name']}");
}
return Text("loading");
},
);
StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection('prosperitybank')
.doc('OHViYK8Zz6XfKGJsSXRL')
.collection('Project Information')
.snapshots(), //key spot fV or email fix
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data?.docs.length,
itemBuilder: (BuildContext context, int index) {
QueryDocumentSnapshot<Object?>? documentSnapshot =
snapshot.data?.docs[index];
//for date/time DateTime mydateTime = documentSnapshot['created'].toDate();
return InkWell(
onTap: () {
Navigator.of(context)
.push(
MaterialPageRoute(
builder: (context) => ViewProperties(documentSnapshot,
snapshot.data?.docs[index].reference)),
)
.then((value) {
setState(() {});
});
},
child: Card(
child: Container(
child: Padding(
padding: const EdgeInsets.all(15.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"${documentSnapshot!['address']}",
style: TextStyle(
fontSize: 24.0,
fontFamily: "lato",
fontWeight: FontWeight.bold,
color: Colors.black),
),
Container(
alignment: Alignment.centerRight,
child: Text(
"${documentSnapshot!['projectcomplete'].toString() + "% Complete"}",
// for mydateTime.toString(),
style: TextStyle(
fontSize: 17.0,
fontFamily: "lato",
color: Colors.black87),
),
)
],
),
),
),
),
);
},
);
} else {
return Center(
child: Text("Loading..."),
);
}
},
),
);
}
}
String uuid;
Future<List<Map<String, dynamic>>> _onQuery() {
Future<List<Map<String, dynamic>>> res;
if (uuid != null) {
res = future.get().then((v) => v.docs
.map((e) => e.data())
.where((e) =>
e['uuid'].toLowerCase().contains(uuid))
.toList());
} else {
res = future.get().then((v) => v.docs.map((e) => e.data()).toList());
}
setState(() {});
return res;
}
now you can use _onQuery as stream.
i want to create list from firestore data
to add suggestions/autocomplete in text field, any help please?
example list:
static final List<String> commodity = [
'Banana',
'Mango',
'Orange',
];
wanted list from firestore:
static getSuggestion(String suggestion) async =>
await FirebaseFirestore.instance
.collection("QFS")
.where('commodity', isEqualTo: suggestion)
.get()
.then((snap) {
return snap.docs;
});
}
my whole code:
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:flutter_typeahead/flutter_typeahead.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
class MyApp extends StatefulWidget {
MyApp({Key? key}) : super(key: key);
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
var _selectedCommodity1;
var _selectedCommodity2;
TextEditingController commodityFiled1 = TextEditingController();
TextEditingController commodityFiled2 = TextEditingController();
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(
title: const Text("QFS"),
),
body: Form(
child: Container(
padding: const EdgeInsets.only(
top: 5, bottom: 10, right: 20, left: 20),
child: Column(children: [
TypeAheadFormField(
textFieldConfiguration: TextFieldConfiguration(
controller: commodityFiled1,
decoration: const InputDecoration(
labelText: 'Commodity1',
icon: Icon(
Icons.receipt_rounded,
color: Colors.black87,
))),
suggestionsCallback: (pattern) {
return CommodityService.getSuggestions(pattern);
},
itemBuilder: (context, String suggestion) {
return ListTile(
title: Text(suggestion),
);
},
transitionBuilder:
(context, suggestionsBox, controller) {
return suggestionsBox;
},
onSuggestionSelected: (String suggestion) {
commodityFiled1.text = suggestion;
},
onSaved: (value) => _selectedCommodity1 = value!,
),
TypeAheadFormField(
textFieldConfiguration: TextFieldConfiguration(
controller: commodityFiled2,
decoration: const InputDecoration(
labelText: 'Commodity2',
icon: Icon(
Icons.receipt_rounded,
color: Colors.black87,
))),
suggestionsCallback: (pattern) {
return getSuggestion(pattern);
},
itemBuilder: (context, dynamic suggestion) {
return ListTile(
title: Text(suggestion),
);
},
transitionBuilder:
(context, suggestionsBox, controller) {
return suggestionsBox;
},
onSuggestionSelected: (dynamic suggestion) {
commodityFiled2.text = suggestion;
},
onSaved: (value) => _selectedCommodity2 = value!,
),
])))));
}
static getSuggestion(String suggestion) async =>
await FirebaseFirestore.instance
.collection("QFS")
.where('commodity', isEqualTo: suggestion)
.get()
.then((snap) {
return snap.docs;
});
}
class CommodityService {
static final List<String> commodity = [
'Banana',
'Mango',
'Orange',
];
static List<String> getSuggestions(String query) {
List<String> matches = [];
matches.addAll(commodity);
matches.retainWhere((s) => s.toLowerCase().contains(query.toLowerCase()));
return matches;
}
}
Clarification: From my understanding, you have documents in your collection that each of them has the property commodity (type String) and you want to fetch only the document that has a specific commodity.
Open your Google Firestore Database dashboard and check which types you can have the documents of your collection.
Click Add document to check the types.
Check the Firestore docs.
Inside snapshot.data, there's docs (every document of your collection).
The code is from docs:
#override
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: _usersStream,
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return Text('Something went wrong');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return Text("Loading");
}
return ListView(
children: snapshot.data!.docs.map((DocumentSnapshot document) {
Map<String, dynamic> data = document.data()! as Map<String, dynamic>;
return ListTile(
title: Text(data['full_name']),
subtitle: Text(data['company']),
);
}).toList(),
);
},
);
}
The code above shows how to convert every document (type DocumentSnapshot) to a JSON format (that can be represented with Map<String, dynamic>). To access to your field commodity (type String), put this data['commodity']. For doc id, you'll need access with document.id, because it isn't inside the document.data() method.
Then on suggestionCallback (I read TypeAheadField of flutter_typeahead API, you have to return to it a List (it's an Iterable) of some type (like List<String>).
I never tried this Flutter package before, but I would edit getSuggestion():
static Future<List<String>> getSuggestion(String suggestion) async =>
return await FirebaseFirestore.instance
.collection("QFS")
.where('commodity', isEqualTo: suggestion)
.get()
.then((snap) {
final docs = snap.data!.docs;
return docs.map((DocumentSnapshot document) {
Map<String, dynamic> data = document.data()! as Map<String, dynamic>;
return data['<fieldname of your document, e.g., "country" or "commodity"'];
}).toList(),
});
Write your solution on comments section.