Flutter reset DropdownButton items after select one of them item - flutter

in this simple DropdownButton widget when i select one item, items refreshed and select value is first item of SessionsEntity list items, and i can't select another item,selecting them cause of select first item,
I think after selecting item, that cause of rebuild DropdownButton widget
SessionsEntity sessionData;
BarCodesBloc _barcodesBloc;
...
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
appBar: ApplicationAppBar(appBarTitle: sessionData!=null? ' (${sessionData.sessionName}) ':'',),
body: BlocListener(
bloc: _barcodesBloc,
listener: (BuildContext context, BarCodesState state) {
if (state is BarCodeScannedSuccessful) {
player.play('ringtones/2.mp3');
}
if (state is BarCodeScannedError) {
}
if (state is BarCodeScannedDuplicate) {
player.play('ringtones/1.mp3');
}
},
child: BlocBuilder(
bloc: _barcodesBloc,
builder: (BuildContext context, BarCodesState state) {
return FutureBuilder(
future: globals.database.sessions.getAllSessionsFuture(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
List<SessionsEntity> sessions = snapshot.data;
List<DropdownMenuItem<SessionsEntity>> _dropdownMenuItems;
if (sessions != null && sessions.length > 0) {
_dropdownMenuItems = buildDropdownMenuItems(sessions);
sessionData = _dropdownMenuItems[0].value;
return Stack(
children: <Widget>[
DropdownButtonHideUnderline(
child: Theme(
data: Theme.of(context).copyWith(
canvasColor: Colors.white,
),
child: DropdownButton(
items: _dropdownMenuItems,
isDense: true,
value:sessionData,
onChanged: onChangeDropdownItem,
isExpanded: true,
hint: Text('please select item',
style: Theme.of(context).textTheme.caption.copyWith(color: Colors.black, )),
),
),
),
],
);
} else {
return Container(
child: Center(
child: Text(
Fa.keywords['noAnySessions'],
style: Theme.of(context).textTheme.caption.copyWith(
color: Colors.black,
),
),
),
);
}
} else {
return Container(
child: Center(
child: Text(
Fa.keywords['noAnySessions'],
style: Theme.of(context).textTheme.caption.copyWith(
color: Colors.black,
),
),
),
);
}
},
);
},
),
),
);
}
onChangeDropdownItem(SessionsEntity selectedCompany) {
setState(() {
sessionData = selectedCompany;
print(sessionData.sessionName);
});
}

Related

Flutter, how to return different widget based on future value?

I would like to base on a future bool value, to set different icons pass back to a data card inside a list, I tried .then or FutureBuilder, but still not successful.
Scaffold:
child: ListView.builder(
itemCount: fullList.length,
itemBuilder: (BuildContext context, int index) {
return dataCard(context, fullList, index);
}),
dataCard:
Row(
children: [
Expanded(
flex: 8,
child: Text(dl[i].Name,
style:
TextStyle(color: Colors.blue[400], fontSize: 16)),
),
Expanded(
flex: 1,
child: setFavouriteIcon(dl[i].ID),
),
],
),
setFavouriteIcon:
Widget setFavouriteIcon(_id) {
final marked = markedFavourites(_id).then((value) { //markedFavourites returns Future<bool>
if (value == true) {
return Icon(
size: 24,
Icons.favorite,
color: Colors.red,
);
} else {
return Icon(
size: 24,
Icons.favorite_border_outlined,
color: Colors.red,
);
}
});
return Text(''); //Without this line, Error: A non-null value must be returned
}}
You can include other state as well on FutureBuilder
Widget setFavouriteIcon(_id) {
return FutureBuilder(
future: markedFavourites(_id),// you shouldn't call method directly here on statefulWidget case
builder: (context, snapshot) {
final value = snapshot.hasData && (snapshot.data as bool? ?? false);
if (value == true) {
return Icon(
size: 24,
Icons.favorite,
color: Colors.red,
);
} else {
return Icon(
size: 24,
Icons.favorite_border_outlined,
color: Colors.red,
);
}
},
);
}
you should use FutureBuilder
class FavoriteWidget extends StatelessWidget {
const FavoriteWidget({super.key});
// some future value
Future<bool> markedFavorites() async {
//do smth
return true;
// or return false
}
#override
Widget build(BuildContext context) {
return Center(
child: FutureBuilder<bool>(
future: markedFavorites(),
builder: (BuildContext context, AsyncSnapshot<bool> snapshot) {
if (snapshot.hasData) {
if (snapshot.data!) {
return const Icon(
Icons.favorite,
color: Colors.red,
);
}
return const Icon(Icons.favorite_border_outlined);
}
},
),
);
}
}

The element type 'Future<Widget>' can't be assigned to the list type 'Widget'

I tried couple of solutions given but nothing worked for me
//this basically lays out the structure of the screen
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Container(
alignment: Alignment.center,
padding: const EdgeInsets.only(
top: 30,
bottom: 60,
),
child: Column(
children: [
buildTitle(),
SizedBox(
height: 50,
),
buildForm(),
Spacer(),
buildBottom(),
],
),
),
);
}
//problem is with buildForm
Future<Widget> buildForm() async {
final valid = await usernameCheck(this.username);
return Container(
width: 330,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
),
child: Form(
key: _userNameformKey,
child: TextFormField(
textAlign: TextAlign.center,
onChanged: (value) {
_userNameformKey.currentState.validate();
},
validator: (value) {
if (value.isEmpty ) {
setState(() {
onNextButtonClick = null;
});
}
else if(!valid){
setState(() {
//user.user.username=value;
onNextButtonClick = null;
showDialog(
context: context,
builder: (context) =>
new AlertDialog(
title: new Text('Status'),
content: Text(
'Username already taken'),
actions: <Widget>[
new ElevatedButton(
onPressed: () {
Navigator.of(context, rootNavigator: true)
.pop(); // dismisses only the dialog and returns nothing
},
child: new Text('OK'),
),
],
),
);
Try using FutureBuilder<T>
Widget build(_) {
return FutureBuilder<bool>(
future: usernameCheck(this.username),
builder: (BuildContext context, AsyncSnapshot<bool> snapshot) {
if(!snapshot.hasData) { // not loaded
return const CircularProgressIndicator();
} else if(snapshot.hasError) { // some error
return const ErrorWidget(); // create this class
} else { // loaded
bool valid = snapshot.data;
return Container(/*...details omitted for conciseness...*/);
}
}
)
}

How can I show alert dialog on base of a stream in case network request fails

Here goes the code I have so far.
_mBlock.mSpotStream is a network request.
I am interested how can I show alert dialog in case _mBlock.getSpots() fails with a network error, while keeping list on screen. I have tried returning alert dialog as a widget, but in this case I can't close it.
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(Strings.of(context).spot_list_title), centerTitle: true),
body: Container(
child: Column(
children: [
Expanded(
child: Stack(
children: [
StreamBuilder<List<SpotDto>>(
stream: _mBlock.mSpotStream,
builder: (context, snapshot) {
return RefreshIndicator(
onRefresh: () {
return _mBlock.getSpots();
},
child: ListView.builder(
itemCount: snapshot.data?.length ?? 0,
itemBuilder: (context, position) {
return SpotListItem(snapshot.data[position], () {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(position.toString())));
});
},
),
);
},
),
Column(
children: [
Expanded(
child: StreamBuilder<Progress<bool>>(
stream: _mBlock.mStateStream,
builder: (context, snapshot) {
return Visibility(
visible: snapshot.data?.mIsLoading ?? false,
child: SizedBox.expand(
child: Container(
color: Colors.blue.withOpacity(Dimens.overlayOpacity),
child: Center(
child: CircularProgressIndicator(),
),
),
),
);
},
),
)
],
)
],
))
],
)),
);
}
}
showAlertDialog(BuildContext context, SpotListBlock block) {
StreamBuilder<Error<String>>(
stream: block.mErrorStream,
builder: (context, snapshot) {
if (snapshot.hasData) {
return AlertDialog(
title: Text(Strings.of(context).error),
content: Text(snapshot.data.mErrorMessage),
actions: [
FlatButton(
child: Text("Cancel"),
onPressed: () {
Navigator.pop(context, true);
},
)
],
);
} else {
return Row();
}
},
);
}
In the end, I have fixed it like this, it was the only way I was able to fix this, any reviews are appreciated:
My fix is based on this gist https://gist.github.com/felangel/75f1ca6fc954f3672daf7962577d56f5
class SpotListScreen extends StatelessWidget {
final SpotListBlock _mBlock = SpotListBlock();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(Strings.of(context).spot_list_title), centerTitle: true),
body: Container(
child: Column(
children: [
Expanded(
child: Stack(
children: [
StreamBuilder<List<SpotDto>>(
stream: _mBlock.mSpotStream,
builder: (context, snapshot) {
return RefreshIndicator(
onRefresh: () {
return _mBlock.getSpots();
},
child: ListView.builder(
itemCount: snapshot.data?.length ?? 0,
itemBuilder: (context, position) {
return SpotListItem(snapshot.data[position], () {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(position.toString())));
});
},
),
);
},
),
StreamBuilder<Error<String>>(
stream: _mBlock.mErrorStream,
builder: (context, snapshot) {
if (snapshot.hasData) {
SchedulerBinding.instance.addPostFrameCallback((_) {
showDialog(
context: context,
barrierDismissible: false,
builder: (_) {
return Scaffold(
body: Center(
child: RaisedButton(
child: Text('dismiss'),
onPressed: () {
Navigator.pop(context);
},
),
),
);
},
);
});
return Container(
width: 0.0,
height: 0.0,
);
} else {
return Container(
width: 0.0,
height: 0.0,
);
}
},
),
Column(
children: [
Expanded(
child: StreamBuilder<Progress<bool>>(
stream: _mBlock.mStateStream,
builder: (context, snapshot) {
return Visibility(
visible: snapshot.data?.mIsLoading ?? false,
child: SizedBox.expand(
child: Container(
color: Colors.blue.withOpacity(Dimens.overlayOpacity),
child: Center(
child: CircularProgressIndicator(),
),
),
),
);
},
),
)
],
)
],
))
],
)),
);
}
}
bloc code
Future<List<SpotDto>> getSpots() {
var completer = new Completer<List<SpotDto>>();
_reportsRepositoryImpl.getSpots().single.then((spotList) {
addNewSpotsToList(spotList);
completer.complete(spotList);
}).catchError((Object obj) {
switch (obj.runtimeType) {
case DioError:
_mErrorSink.add(Error((obj as DioError).message));
completer.complete();
break;
default:
completer.complete();
}
_mSpotSink.add(_mSpotList);
});
return completer.future;
}
Showing an alert dialog is just a simple call, e.g.:
await showDialog<bool>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: 'alert!!!',
content: 'hello world',
actions: [
FlatButton(child: Text('cancel'), onPressed: () => Navigator.pop(context, false)),
FlatButton(child: Text('ok'), onPressed: () => Navigator.pop(context, true)),
],
);
},
)
when you call showDialog a dialog will be shown on the screen.

Flutter: using button to input a static text

I want to make list of button with a static text e.g(lorem, ipsum, etc) for a search bar, so when i hit the button it will input the text into textfield. can it be done in flutter?
Use SeachDelegate delegate
class CustomSearchHintDelegate extends SearchDelegate {
CustomSearchHintDelegate({
String hintText,
}) : super(
searchFieldLabel: hintText,
keyboardType: TextInputType.text,
textInputAction: TextInputAction.search,
);
#override
Widget buildLeading(BuildContext context) => Text("leading");
#override
Widget buildSuggestions(BuildContext context) => Text("suggestions");
#override
Widget buildSuggestions(BuildContext context) { // This is your list which comes from futurebuilder or streambuilder
// BookModel bookModel = Provider.of(context);
if (query.isNotEmpty)
// return list of books which student wants to search
return Consumer<BookModel>(
builder: (_, bookModel, __) {
return FutureBuilder(
future: bookModel.searchOperation(query),
builder: (BuildContext context, AsyncSnapshot snapshot) {
// switch (snapshot.connectionState) /{
// case ConnectionState.waiting:
// return Center(
// child: CircularProgressIndicator(),
// );
// break;
// case ConnectionState.none:
// return Text("Something went wrong");
// break;
// case ConnectionState.done:
// case ConnectionState.active:
if (!snapshot.hasData)
return Center(
child: CircularProgressIndicator(),
);
return snapshot.hasData
? snapshot.data.length != 0
? ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return InkWell(
onTap: () {
final box = Hive.box("Student");
snapshot.data[index].postedBy != box.get("ID")
? Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => BookDetail(
book: snapshot.data[index],
),
),
)
: Navigator.push(
context,
MaterialPageRoute(
builder: (con) =>
MyPostedBookDetail(
book: snapshot.data[index],
),
),
);
},
child: ListTile(
title: Text(
snapshot.data[index].bookName.toString(),
),
),
);
},
)
: Column(
crossAxisAlignment: CrossAxisAlignment.center,
// mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SizedBox(
height: 50,
),
Center(
child: Text(
"No results found for '$query'",
textAlign: TextAlign.center,
style: TextStyle(fontSize: 18),
),
),
],
)
: Column(
crossAxisAlignment: CrossAxisAlignment.center,
// mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SizedBox(
height: 50,
),
Center(
child: Text(
"No results found for '$query'",
textAlign: TextAlign.center,
style: TextStyle(fontSize: 18),
),
),
],
);
// break;
}
// },
);
},
);
return Container();
}
#override
List<Widget> buildActions(BuildContext context) => [];
}

How can I access the innermost documents in nested collection structures?

As seen in the picture, there is a collection structure within the firestore. I want to show it with a listview by reaching the document information at the end. But I can't view it on the screen.
Code here:
#override
Widget build(BuildContext context) {
randevular = databaseRef
.collection(
'kuaforumDB/$_salonID/BekleyenRandevular/')
.snapshots();
return StreamBuilder<QuerySnapshot>(
stream: randevular,
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if(!snapshot.hasData) {
return Column(
children:<Widget> [
SizedBox(
height: 100,
),
Center(
child: Image.asset("assets/images/icons/fon.webp",matchTextDirection: true,
height: 140.0,
width: 140.0,
),),
SizedBox(
height: 20
),
Center(
child: new Text('Henüz bir randevu oluşturmadınız.')
)
],
);
}
else if (snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: new Center(
child: new CircularProgressIndicator(
value: null,
strokeWidth: 7.0,
),
)
);
} else {
return ListView(
children: snapshot.data.documents
.map((document) {
var query = databaseRef
.collection('kuaforumDB/')
.document('$_salonID')
.collection('BekleyenRandevular')
.document(document.documentID)
.collection('get')
.snapshots();
return StreamBuilder<QuerySnapshot> (
stream: query,
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot2){
if (!snapshot2.hasData) return Text("Loading...");
return ListView(
children: snapshot2.data.documents
.map((DocumentSnapshot doc) => Card(
child: ListTile(
leading: IconButton(
tooltip: '',
icon: const Icon(Icons.check_circle, color: Colors.red,),
color: doc['randevuTarih']
.toDate()
.isBefore(DateTime.now())
? Colors.green
: Colors.orangeAccent,
iconSize: 30,
onPressed: () {},
),
title: Text(AppConstants.formatter
.format((doc['randevuTarih'].toDate())
.add(Duration(hours: 0)))
.toString()),
subtitle: Text('Randevu Onay Bekleniyor.'),
trailing: Icon(Icons.keyboard_arrow_right,
color: Colors.grey, size: 30.0),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (content) => MyPendingDetailPage(
salonID: _salonID.toString(),
userID: mPhone,
randevuID:
doc.documentID.toString(),
randevuTarih: AppConstants
.formatter
.format((doc['randevuTarih']
.toDate())
.add(Duration(hours: 0)))
.toString(),
randevuHizmet: doc['hizmetler'],
randevuFiyat:
doc['fiyat'].toString(),
randevuSure:
doc['sure'].toString(),
randevuFavori:
doc['favori'] == null
? false
: doc['favori'],
randevuBittimi:
doc['randevuTarih']
.toDate()
.isBefore(
DateTime.now())
? true
: false,
ayBasi: startofmonth,
sonrandevu : doc['randevuTarih'],
)));
}, )))
.toList(),
);
},
);
}).toList());
}
});
}
Using nested Listview in the code above may have caused a question. But I don't know how to solve this. When I check it, I see that I can actually pull the data, but I can't show it on the screen.