Flutter dropdownbutton changed value is not displaying - flutter

I'm new in flutter. I've created a simple project.
It is fetching documents of person collection from cloud firestore.
There is a modal screen to create new person document (it is opening When I touch the + button)
I have a problem In that modalBottomSheet
I can see the new value of department dropDownButton on the log screen but user interface are not changing.
I think it is related to 'context' but I couldn't solve the problem
Here is my code:
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
class Person extends StatefulWidget {
const Person({Key? key}) : super(key: key);
#override
_PersonState createState() => _PersonState();
}
class _PersonState extends State<Person> {
final TextEditingController _nameController = TextEditingController();
final CollectionReference _person = FirebaseFirestore.instance.collection('person');
final CollectionReference _department = FirebaseFirestore.instance.collection('department');
String? _usersDeptName;
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
children: [
Expanded(
child: StreamBuilder(
stream: _person.snapshots(),
builder: (context, AsyncSnapshot<QuerySnapshot> streamSnapshot) {
if (streamSnapshot.hasData) {
return ListView.builder(
itemCount: streamSnapshot.data!.docs.length,
itemBuilder: (context, index) {
final DocumentSnapshot documentSnapshot = streamSnapshot.data!.docs[index];
return Card(
margin: const EdgeInsets.all(10),
child: ListTile(
title: Text(documentSnapshot['personName']),
subtitle: Text(documentSnapshot['departmentName'] ?? '?'),
),
);
},
);
}
return const Center(
child: CircularProgressIndicator(),
);
},
),
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => {_create()},
child: const Icon(Icons.add),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat);
}
Future<void> _create() async {
_usersDeptName = null;
await showModalBottomSheet(
isScrollControlled: true,
context: context,
builder: (BuildContext ctx) {
return Padding(
padding: EdgeInsets.only(top: 20, left: 20, right: 20, bottom: MediaQuery.of(context).viewInsets.bottom + 20),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextField(controller: _nameController, decoration: InputDecoration(labelText: 'person_name'.tr())),
const SizedBox(height: 10),
StreamBuilder<QuerySnapshot>(
stream: _department.snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Text("loading").tr();
} else {
List<DropdownMenuItem> departments = [];
int? howManyRecords = snapshot.data?.size;
for (int i = 0; i < howManyRecords!; i++) {
DocumentSnapshot snap = snapshot.data?.docs[i] as DocumentSnapshot<Object?>;
departments.add(DropdownMenuItem(child: Text(snap.get('departmentName')), value: snap.get('departmentName')));
}
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
child: DropdownButton(
value: _usersDeptName,
items: departments,
onChanged: (newValue) {
setState(() {
_usersDeptName = newValue.toString();
print('$_usersDeptName is selected');
});
},
isExpanded: true,
),
),
],
);
}
},
),
ElevatedButton(
child: const Text('save').tr(),
onPressed: () async {
final String name = _nameController.text;
if (name != null) {
await _person.add({"personName": name, 'departmentName': _usersDeptName});
_nameController.text = '';
Navigator.of(context).pop();
}
},
)
],
),
);
},
);
}
}

Try using StatefulBuilder to update the bottomSheet state.
Future<void> _create() async {
_usersDeptName = null;
await showModalBottomSheet(
isScrollControlled: true,
context: context,
builder: (BuildContext ctx) {
return StatefulBuilder(
builder: (context, setState) => Padding(
padding: EdgeInsets.only(
If you like to change the widget-state(main UI) at the same time, you can rename the StatefulBuilder's setState and call both on onChanged.

when setting up the value for the _usersDeptName, you are converting it to string, this is not right because now the items and the selected items is 2 different thing,
so if you want it to be the department name, then when setting up the value for _usersDeptName, make it bind to the department name:
example
setState(() {
_usersDeptName = newValue.departmentName;
print('$_usersDeptName is selected');
});

Related

How can i separate the selected item on a DropDownButton?

Right now I'm trying to make a dialog where I can change the role for the users that has been logged in on my app, for that i have a ListView with Cards that shows up the information of the user and a DropDownButton to select the role that a can assign to that user.
But when i select a role or item it change on all the user's Card.
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
final user = FirebaseAuth.instance.currentUser!;
String role = '1';
Stream<QuerySnapshot> readUsers() =>
FirebaseFirestore.instance.collection('users').snapshots();
Future accDialog(BuildContext context) => showDialog(
context: context,
builder: (context) => StatefulBuilder(
builder: (BuildContext context, setState) => Dialog(
child: Container(
margin: const EdgeInsets.all(24),
child: StreamBuilder(
stream: readUsers(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
roleFunction(String q) {
setState(() {
role = q;
});
}
if (snapshot.hasError) {
return const Text('Algo ha salido mal!');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return const Text("Cargando");
}
return ListView(
shrinkWrap: true,
children: snapshot.data!.docs
.map((DocumentSnapshot document) {
Map<String, dynamic> data =
document.data()! as Map<String, dynamic>;
return Card(
margin: const EdgeInsets.all(8),
child: Padding(
padding: const EdgeInsets.all(8),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
data['name'],
style:
Theme.of(context).textTheme.titleMedium,
),
Text('Correo: ${data['email']}'),
Text('Rol Actual: ${data['role']}'),
Row(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: RoleMenu(
roleFunction: roleFunction)),
const Spacer(),
ElevatedButton(
onPressed: () {
final setRole = <String, String>{
"role": role,
};
FirebaseFirestore.instance
.collection('users')
.doc(document.id)
.set(setRole,
SetOptions(merge: true));
},
child: const Text('Guardar'))
],
)
],
),
),
);
})
.toList()
.cast(),
);
},
),
),
),
),
);
class RoleMenu extends StatefulWidget {
final Function roleFunction;
const RoleMenu({super.key, required this.roleFunction});
#override
State<RoleMenu> createState() => _RoleMenuState();
}
class _RoleMenuState extends State<RoleMenu> {
String selected = '';
#override
Widget build(BuildContext context) {
return DropdownButtonHideUnderline(
child: DropdownButton<String>(
enableFeedback: true,
dropdownColor: ElevationOverlay.applySurfaceTint(
Theme.of(context).colorScheme.surface,
Theme.of(context).colorScheme.surfaceTint,
2),
iconEnabledColor: Theme.of(context).colorScheme.onSurfaceVariant,
style: Theme.of(context).textTheme.labelLarge,
items: const [
DropdownMenuItem<String>(
value: 'User',
child: Text('Usuario'),
),
DropdownMenuItem<String>(
value: 'Admin',
child: Text('Admin'),
),
],
value: selected,
onChanged: (value) {
setState(() {
selected = value!;
widget.roleFunction(selected);
});
},
),
);
}
}

Filter StreamBuilder snapshot data - flutter

When the user enters 1st value and 2nd value and clicks the submit button, StreamBuilder should output the data between the values (age filter). How to do this?
Here is picture of display. and my code - below the picture
Full page code:
class TestScreen extends StatefulWidget {
const TestScreen({super.key});
#override
State<TestScreen> createState() => _TestScreenState();
}
class _TestScreenState extends State<TestScreen> {
final _firstTextController = TextEditingController();
final _secondTextController = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: SafeArea(
child: StreamBuilder(
stream: FirebaseFirestore.instance
.collection("users")
// .orderBy("accountCreated")
.where("activeStatus", isEqualTo: "active")
// .where("uid", isNotEqualTo: FirebaseAuth.instance.currentUser!.uid)
.snapshots(),
builder: (context, AsyncSnapshot<QuerySnapshot> streamSnapshot) {
if (streamSnapshot.connectionState == ConnectionState.waiting ||
!streamSnapshot.hasData) {
return const Center(
child: CircularProgressIndicator(),
);
}
return ListView.builder(
itemCount: streamSnapshot.data!.docs.length + 1,
itemBuilder: (context, index) {
if (index == 0) {
return Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
controller: _firstTextController,
decoration: const InputDecoration(
hintText: "from",
),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
controller: _secondTextController,
decoration: const InputDecoration(
hintText: "to",
),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: TextButton(
onPressed: () {
print("First value: ${_firstTextController.text}");
print(
"Second value: ${_secondTextController.text}");
},
child: const Text("Submit"),
),
),
],
);
} else {
return Text(
streamSnapshot.data!.docs[index - 1]["displayName"]);
}
},
);
},
),
),
);
}
}
class TestScreen extends StatefulWidget {
const TestScreen({super.key});
#override
State<TestScreen> createState() => _TestScreenState();
}
class _TestScreenState extends State<TestScreen> {
int userLowerValue = 20;
int userUpperValue = 60;
Stream<QuerySnapshot> getDataStream(int lower, int upper) {
return FirebaseFirestore.instance
.collection("users")
.where("age", isGreaterThan: lower)
.where("age", isLessThan: upper)
.snapshots();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: [
IconButton(
onPressed: () {
int lower = 20;
int upper = 60;
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text("Enter Filter Values"),
content: SingleChildScrollView(
child: ListBody(
children: <Widget>[
TextFormField(
keyboardType: TextInputType.number,
decoration:
InputDecoration(labelText: "Lower Value"),
onChanged: (value) {
lower = int.parse(value);
},
),
TextFormField(
keyboardType: TextInputType.number,
decoration:
InputDecoration(labelText: "Upper Value"),
onChanged: (value) {
upper = int.parse(value);
},
),
],
),
),
actions: <Widget>[
TextButton(
child: Text("Apply"),
onPressed: () {
Navigator.of(context).pop();
setState(() {
userLowerValue = lower;
userUpperValue = upper;
});
},
),
],
);
},
);
},
icon: const Icon(Icons.filter))
],
),
body: SafeArea(
child: StreamBuilder(
stream: getDataStream(userLowerValue, userUpperValue),
builder: (context, AsyncSnapshot<QuerySnapshot> streamSnapshot) {
if (streamSnapshot.connectionState == ConnectionState.waiting ||!streamSnapshot.hasData) {
return const Center(
child: CircularProgressIndicator(),
);
}
if (streamSnapshot.data!.docs.isEmpty) {
return const Center(
child: CustomText(
text: "No results found",
size: 30,
),
);
}
return ListView.builder(
itemCount: streamSnapshot.data!.docs.length,
itemBuilder: (context, index) {
return Text(streamSnapshot.data!.docs[index]["displayName"]);
},
);
},
),
),
);
}
}

Flutter : i want to pass (title,details,content) to details page display it in vertically in top of the details page?

eg: details about the questions ......................................................when i click to a gridview item i want to pass (title,details,content) to details page display in vertically in top of the details page but when i am pass the data not able to fetch the data in details page i created a constrctor in details page not able to set the data in text and image.
Home Page
----------
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'DetailsPage.dart';
var paddingBottom = 48.0;
class HomePage extends StatelessWidget {
final String apiUrl = "https://www.sofikart.com/MobileApi/banners";
final String apiUrl1 =
"https://wayindia.net/indigo/odia_rashifal/rasifhala.php";
Future<List<dynamic>> fetchUsers() async {
var result = await http.get(Uri.parse(apiUrl1));
return json.decode(result.body)['data'];
}
String id(dynamic user) {
return user['id'];
}
String title(dynamic user) {
return user['title'];
}
String content(dynamic user) {
return user['content'];
}
String eng_title(dynamic user) {
return user['eng_title'];
}
String main_img(dynamic user) {
return user['main_img'];
}
String image_2(dynamic user) {
return user['image_2'];
}
String image_3(dynamic user) {
return user['image_3'];
}
String image_4(dynamic user) {
return user['image_4'];
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('ଆଜିର ରାଶିଫଳ'),
centerTitle: true,
),
body: Container(
child: FutureBuilder<List<dynamic>>(
future: fetchUsers(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
print(id(snapshot.data[0]));
return GridView.builder(
itemCount: snapshot.data.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
crossAxisSpacing: 20,
mainAxisSpacing: 25,
),
padding: EdgeInsets.all(13),
shrinkWrap: true,
itemBuilder: (ctx, index) {
return InkWell(
child: Container(
decoration: BoxDecoration(
color: Colors.transparent,
borderRadius: BorderRadius.all(Radius.circular(12))),
child: Column(
children: [
Expanded(
flex: 9,
child: ClipRRect(
borderRadius:
BorderRadius.all(Radius.circular(12)),
child: Image.network(
snapshot.data[index]['main_img'],
fit: BoxFit.fill)),
),
Expanded(
flex: 2,
child: Text(
title(snapshot.data[index]),
style: TextStyle(
color: Colors.black, fontSize: 17),
)),
],
),
),
onTap: () {
print("Click event on Container");
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(builder: (context) => DetailsPage()), (route) => false);
},
);
},
);
} else {
return Center(child: CircularProgressIndicator());
}
},
),
),
);
}
}
Details Page
------------
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:odia_rasiphala/HomePage.dart';
import 'dart:convert';
class DetailsPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async => false,
child: new Scaffold(
appBar: new AppBar(
title: new Text('ଆଜିର ରାଶିଫଳ'),
leading: new IconButton(
icon: new Icon(Icons.arrow_back_outlined),
onPressed: () => Navigator.pushReplacement(context,
new MaterialPageRoute(builder: (context) => HomePage())),
),
actions: [
IconButton(
onPressed: () {},
icon: Icon(Icons.share),
),
],
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Image.network(
'',
width: 200.0,
height: 200.0,
),
new Center(
child: new Text('',style: TextStyle(
color: Colors.black,fontSize: 17
)),
)
],
),
));
}
}
I am guessing you want to pass "eng_title" and "main_img" to details screen.
To do that first make a constructor in your details pages. Example:
class DetailScreen extends StatelessWidget {
// In the constructor, require a Todo.
const DetailScreen({Key? key, required this.eng_title, required this.main_img}) : super(key: key);
// Declare a field that holds the strings passed to this class.
final String eng_title;
final String main_img;
#override
Widget build(BuildContext context) {
// Use the final parameters to create the UI.
return Scaffold(
appBar: AppBar(
title: Text(eng.title),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Text(main_img),
),
);
}
}
on your OnTap function, when you click an item on the list, just pass the required parameters like this
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DetailScreen(eng_title: snapshot.data[index]['eng_title'], main_img: snapshot.data[index]['main_img']),
),
);
},
This way you can pass data from onescreen to another. Do not use push and remove until, if you want the user to go back to the list in homepage.
For more info about passing data read the following article by flutter:
https://docs.flutter.dev/cookbook/navigation/passing-data

error: The argument type 'Null' can't be assigned to the parameter type 'Map<String, dynamic>'

I am writing my first Flutter App with some online tutorials and I found error that I can't fix it.
I am trying to add Navigation by Navigator, but I can't understand why it doesn't work.
Once I am using Navigator in GestureDetector and it works fine, but I don't know what I supposed to do in floatingActionButton to make it work the same way. Note(NoteMode.Adding, null) probably should be something else instead null, because this null is making error (error from title). Can someone explain me what I am doing wrong and what I don't undarstand
Note List
#override
_NoteListState createState(){return _NoteListState();}
}
class _NoteListState extends State<NoteList> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Notes"),
),
body: FutureBuilder(
future: NoteProvider.getNoteList(),
builder: (context, AsyncSnapshot snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
final notes = snapshot.data;
return ListView.builder(
itemBuilder: (context, index) {
return GestureDetector(
onTap: () {
Navigator.push(
context, MaterialPageRoute(builder: (context) =>
Note(NoteMode.Editing, (notes as dynamic)[index]))
);
},
child: Card(
child: Padding(
padding: const EdgeInsets.only(
top: 30.0, bottom: 30.0, left: 13, right: 22),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
_NoteTitle((notes as dynamic)[index]['title']),
Container(height: 3,),
_NoteText((notes as dynamic)[index]['text']),
],
),
),
),
);
},
itemCount: notes.length,
);
}
return Center(child: CircularProgressIndicator());
},
),
floatingActionButton: FloatingActionButton(
onPressed: () {
Navigator.push(context, MaterialPageRoute(builder: (context) => Note(NoteMode.Adding, null)));
},
child: Icon(Icons.add),
),
);
}
}
Note
enum NoteMode{
Editing,
Adding
}
class Note extends StatefulWidget{
final NoteMode noteMode;
final Map<String, dynamic> note;
Note(this.noteMode, this.note,);
#override
State<Note> createState() => _NoteState();
}
class _NoteState extends State<Note> {
final TextEditingController _titleController = TextEditingController();
final TextEditingController _textController = TextEditingController();
List<Map<String, String>> get _notes => NoteInheritedWidget.of(context).notes;
#override
void didChangeDependencies(){
if(widget.noteMode == NoteMode.Editing){
_titleController.text = widget.note['title'];
_textController.text = widget.note['text'];
}
super.didChangeDependencies();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
widget.noteMode == NoteMode.Adding ? 'Add note' : 'Edit note',
),
),
body: Padding(
padding: const EdgeInsets.all(40.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextField(
controller: _titleController,
decoration: InputDecoration(
hintText: "Note title",
),
),
Container(height: 8,),
TextField(
controller: _textController,
decoration: InputDecoration(
hintText: "Note text",
),
),
Container(height: 15,),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
_NoteButton('SAVE', Colors.lightBlue, (){
final title = _titleController.text;
final text = _textController.text;
if(widget.noteMode == NoteMode.Adding){
NoteProvider.insertNote({
'title': title,
'text': text
});
} else if (widget.noteMode == NoteMode.Editing){
NoteProvider.updateNote( {
'id': widget.note['id'],
'title': _titleController.text,
'text': _textController.text,
});
}
Navigator.pop(context);}),
_NoteButton('DISCARD', Colors.grey, (){Navigator.pop(context);}),
widget.noteMode == NoteMode.Editing ?
_NoteButton('DELETE', Colors.redAccent, () async {
await NoteProvider.deleteNote(widget.note['id']);
Navigator.pop(context);})
: Container(),
],
)
],
),
),
);
}
}
Either you have to pass Map in place of null because you are receiving a Map on that page
Navigator.push(context, MaterialPageRoute(builder: (context) => Note(NoteMode.Adding, {"key":"value"})));
or you have to make Map nullable as
class Note extends StatefulWidget{
final NoteMode noteMode;
final Map<String, dynamic>? note;
Note(this.noteMode, this.note,);
#override
State<Note> createState() => _NoteState();
}

Passing data to another screen with Flutter Provider

I'm trying to pass the data to another screen using Provider, but it seems I'm always passing on the same data unless I sort the List and then pass the different data (meaning I'm probably switching the index by sorting the list so that is why it's passing different data now). In short, I call the API, populate the list, setting up the provider too for the next page, and on click I list out the the information from the previous screen, but the problem is I display the same item always unless I sort the list. Here is the code:
Calling the API and displaying the list:
var posts = <RideData>[];
var streamController = StreamController<List<RideData>>();
#override
void initState() {
_getRideStreamList();
super.initState();
}
_getRideStreamList() async {
await Future.delayed(Duration(seconds: 3));
var _vehicleStreamData = await APICalls.instance.getRides();
var provider = Provider.of<RideStore>(context, listen: false);
posts = await _vehicleStreamData
.map<RideData>((e) => RideData.fromJson(e))
.toList();
streamController.add(posts);
provider.setRideList(posts, notify: false);
}
bool isSwitched = true;
void toggleSwitch(bool value) {
if (isSwitched == false) {
posts.sort((k1, k2) => k1.rideId.compareTo(k2.rideId));
} else {
posts.sort((k1, k2) => k2.rideId.compareTo(k1.rideId));
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: SafeArea(
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Column(
children: [
TextButton(
child: Text('sort ascending'),
onPressed: () {
setState(() {
toggleSwitch(isSwitched = !isSwitched);
});
}),
Container(
height: 1000,
child: StreamBuilder<List<RideData>>(
initialData: posts,
stream: streamController.stream,
builder: (context, snapshot) {
return ListView.builder(
shrinkWrap: true,
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return Column(
children: [
Row(
children: [
Padding(
padding: const EdgeInsets.only(left: 15.0),
child: Text(
'Ride #${snapshot.data[index].rideId}',
),
),
FlatButton(
textColor: Colors.blue[700],
minWidth: 0,
child: Text('View'),
onPressed: () {
// here is where I pass the data to the RideInfo screen
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => RideInfo(
rideId: snapshot
.data[index].rideId,
)));
},
),
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
'${snapshot.data[index].pickupTime}',
),
Text(
'${snapshot.data[index].jobArrived}',
),
],
),
],
);
},
);
}),
),
],
),
),
),
);
}
After pressing the View button and passing the data to another screen (RideInfo):
class RideInfo extends StatefulWidget {
static const String id = 'ride_info_screen';
String rideId;
RideInfo({#required this.rideId});
#override
_RideInfoState createState() => _RideInfoState();
}
class _RideInfoState extends State<RideInfo> {
String rideID = '';
#override
void initState() {
super.initState();
setState(() {
rideID = widget.rideId;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
title: Text(
'Ride #$rideID',
),
),
body: SafeArea(
child: SingleChildScrollView(
child: Consumer<RideStore>(
builder: (context, rideStore, child) {
return Column(
children: [
ListView.builder(
shrinkWrap: true,
itemCount: 1,
itemBuilder: (context, index) {
RideData rides = rideStore.getRideByIndex(index);
return Column(
children: [
Expanded(
flex: 2,
child: Column(
children: [
Text(
"PICK UP",
),
// here I display the pickUpTime but it is always the same and I wanted to display the time based on the ID
Text(
'${rides.pickupTime}AM',
),
],
),
),
],
);
}),
],
);
},
),
),
),
);
}
}
The data (pickUpTime in this case) doesn't change when I press to see the View of a single item, but like I said, when I change the order of the list with the sort method, then I get the different data.
Here is the Provider model:
class RideStore extends ChangeNotifier {
List<RideData> _rideList = [];
List<RideData> get rideList => _rideList;
setRideList(List<RideData> list, {bool notify = true}) {
_rideList = list;
if (notify) notifyListeners();
}
RideData getRideByIndex(int index) => _rideList[index];
int get rideListLength => _rideList.length;
}
How do I display the correct information based on the ID from the List that I pressed and passed in the Ride Info screen so it doesn't give back always the same data? Thanks in advance for the help!
The offending code is in RideInfo:
ListView.builder(
shrinkWrap: true,
itemCount: 1,
itemBuilder: (context, index) {
RideData rides = rideStore.getRideByIndex(index);
The index is always 1, so you are always showing the first RideData. There are various options to fix it, e.g. pass the index, or even pass the RideData, to the RideInfo constructor:
class RideInfo extends StatefulWidget {
static const String id = 'ride_info_screen';
String rideId;
final int index;
RideInfo({#required this.rideId, #required this.index, Key key})
: super(key: key) {
and:
RideData rides = rideStore.getRideByIndex(widget.index);
I have some additional comments on the code. Firstly, the ListView is serving no purpose in RideInfo, so remove it.
Secondly, there is no need to construct the streamController and to use StreamBuilder in the parent form. Your list is available in the RideStore. So your parent form could have:
Widget build(BuildContext context) {
var data = Provider.of<RideStore>(context).rideList;
...
Container(
height: 1000,
child:
// StreamBuilder<List<RideData>>(
// initialData: posts,
// stream: streamController.stream,
// builder: (context, snapshot) {
// return
ListView.builder(
shrinkWrap: true,
itemCount: data.length,
I hope these comments help.
Edit:
It is simple to edit your code to use FutureBuilder. Firstly, make _getRideStreamList return the data it read:
_getRideStreamList() async {
...
return posts;
}
Remove the call to _getRideStreamList in initState and wrap the ListView in the FutureBuilder that invokes _getRideStreamList:
Container(
height: 1000,
child: FutureBuilder(
future: _getRideStreamList(),
builder: (ctx, snapshot) {
if (!snapshot.hasData) {
return Center(child: CircularProgressIndicator());
} else {
var data = snapshot.data;
return ListView.builder(
...
);
}
},
),
),
This displays the CircularProgressIndicator while waiting for the data.
Note that this is a quick hack - you do not want to read the data everytime that the widget rebuilds. So _getRideStreamList could check if the data has already been read and just return it rather than rereading.