Passing data to another screen with Flutter Provider - flutter

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.

Related

Flutter dropdownbutton changed value is not displaying

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');
});

flutter listview builder inside a listview builder

I don't have much experience with flutter.
I would like to use the language_tool library (https://pub.dev/packages/language_tool) for Dart and Flutter.
To show the data obtained from the tool() function, I created a FutureBuilder with a ListView.builder inside, which returns a Column.
I would like there to be 2 children inside the column:
1- a Text with mistake.issueDescription as text (for each "mistake")
2- another ListView that returns the elements of the List mistake.replacements for each "mistake"
Anyone know how I can fix it?
Below I put the code I created, which works fine until I put the Listview builder inside the first ListView builder.
import 'package:flutter/material.dart';
import 'package:language_tool/language_tool.dart';
void main() => runApp(mainApp());
class mainApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return const MaterialApp(
home: Chat(),
);
}
}
class Chat extends StatefulWidget {
const Chat({Key? key}) : super(key: key);
#override
_ChatState createState() => _ChatState();
}
class _ChatState extends State<Chat> {
String text = 'Henlo i am Gabriele';
Future<List<WritingMistake>> tool(String text) async {
var tool = LanguageTool();
var result = tool.check(text);
var correction = await result;
List<WritingMistake> mistakes = [];
for (var m in correction) {
WritingMistake mistake = WritingMistake(
message: m.message,
offset: m.offset,
length: m.length,
issueType: m.issueType,
issueDescription: m.issueDescription,
replacements: m.replacements,
);
mistakes.add(mistake);
}
print(mistakes.length);
print(mistakes);
return mistakes;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
children: [
Container(
color: Colors.red,
height: 150.0,
width: double.infinity,
child: Center(
child: Text(text, style: const TextStyle(fontSize: 20.0))),
),
FutureBuilder(
future: tool(text),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.data == null) {
return const Center(
child: Text('Loading...'),
);
} else {
return SizedBox(
height: 200.0,
child: ListView.builder(
itemCount: snapshot.data.length,
itemBuilder:
(BuildContext context, int mistakeIdIndex) {
return Column(
children: [
Text(snapshot
.data[mistakeIdIndex].issueDescription),
// this is where the problems begin
ListView.builder(
itemCount: snapshot.data[mistakeIdIndex]
.replacements.length,
scrollDirection: Axis.horizontal,
itemBuilder:
(BuildContext context, int index) {
return Text(snapshot.data[mistakeIdIndex]
.replacements[index]);
}),
],
);
}),
);
}
}),
],
),
),
);
}
}
I hope I was clear and that someone can help me.
Thank you :)
You cannot give a listview-builder as a child for a column try changing the Column widget to a ListView and set its shrinkWrap property to true.
ListView(
children: [
Container(
color: Colors.red,
height: 150.0,
width: double.infinity,
child: Center(
child: Text(text, style: const TextStyle(fontSize: 20.0))),
),
FutureBuilder(
future: tool(text),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.data == null) {
return const Center(
child: Text('Loading...'),
);
} else {
return SizedBox(
height: 200.0,
child: ListView.builder(
shrinkWrap:true,
itemCount: snapshot.data.length,
itemBuilder:
(BuildContext context, int mistakeIdIndex) {
return ListView(
shrinkWrap:true,
children: [
Text(snapshot
.data[mistakeIdIndex].issueDescription),
// this is where the problems begin
ListView.builder(
shrinkWrap:true,
itemCount: snapshot.data[mistakeIdIndex]
.replacements.length,
scrollDirection: Axis.horizontal,
itemBuilder:
(BuildContext context, int index) {
return Text(snapshot.data[mistakeIdIndex]
.replacements[index]);
}),
],
);
}),
);
}
}),
],
),
),

Problem with using icons from cloud firestore in flutter app

I am currently displaying a list of icons in an Alert Dialog within a control_point Daily Button. I want to display all of the buttons I have in my cloud firestore within the Alert Dialog but due to the way I have laid out the code, this means that for each icon in the database, the Daily Goals widget is repeated, as shown in the image below:
Edit: The second image is the desired layout
If you click on the first control point button, it shows the first icon that is in the database and it is repeated 4 times. Then if you click the second control point button, it shows the next icon that is in the database as shown in the images below. This is repeated for all of the icons in the database. I am wanting to have all of the icons in one Alter Dialog.
This is my code:
class _MyHomePageState3 extends State<MyHomePage3> {
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color(0xFF030164),
appBar: AppBar(centerTitle: true, title: Text('Project')),
body: _buildBody(context),
);
}
Widget _buildBody(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance.collection('icons').snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) return LinearProgressIndicator();
return _buildList(context, snapshot.data.docs);
},
);
}
Widget _buildList(BuildContext context, List<DocumentSnapshot> snapshot) {
return ListView(
children: snapshot.map((data) => _buildListItem(context, data)).toList(),
);
}
Widget _buildListItem(BuildContext context, DocumentSnapshot data) {
final record3 = Record3.fromSnapshot(data);
int iconCode = record3.ref;
List<IconData> _iconsDaily = [
Icons.shopping_cart,
Icons.cake_rounded,
Icons.card_giftcard,
Icons.control_point,
];
return Padding(
child: new Container(
child: new ListView(
children: <Widget>[
new Text(
"Daily Goals",
),
new Container(
child: new ListView(
scrollDirection: Axis.horizontal,
children: new List.generate(4, (int index) {
return new Positioned(
child: new DailyButton(
onTap: () {
if (_iconsDaily[index] == Icons.control_point) {
showDialog(
context: context,
builder: (BuildContext context) =>
_buildPopupDialog(context, data),
);
}
},
iconData: _iconsDaily[index]
),
);
}),
),
),
],
),
),
);
}
Widget _buildPopupDialog(BuildContext context, DocumentSnapshot data) {
final record3 = Record3.fromSnapshot(data);
int iconCode = record3.ref;
return new AlertDialog(
content: SingleChildScrollView(
child: new Container(
child: GridView.count(
children: new List.generate(4, (int index) {
return new Positioned(
child: new MoodButton(
onTap: () => print("Mood"),
iconData: (IconData(iconCode, fontFamily: 'MaterialIcons')),
),
);
}),
),
),
),
);
}
}
class Record3 {
final String name;
final int ref;
final bool display;
final DocumentReference reference;
Record3.fromMap(Map<String, dynamic> map, {this.reference})
: assert(map['name'] != null),
assert(map['ref'] != null),
assert(map['display'] != null),
name = map['name'],
ref = map['ref'],
display = map['display'];
Record3.fromSnapshot(DocumentSnapshot snapshot)
: this.fromMap(snapshot.data(), reference: snapshot.reference);
#override
String toString() => "Record<$name:$ref:$display>";
}
If anyone would be able to give any advice for restructing the code so that it only shows one Daily Goals and would be able to include all of the icons within the alert dialog, that would be amazing!

Flutter: Refreshing ListView.Builder with GetX

I am creating the List of Cards according to the number of toDoId.
toDoController.toDo() is like
toDo = [q1, r4, g4, d4].obs;
And, this is my ListView.builder()
Obx(() {
List _todo = toDoController.toDo();
return ListView.builder(
shrinkWrap: true,
scrollDirection: Axis.horizontal,
itemCount: _todo.length,
itemBuilder: (BuildContext context, int i) {
var _loading = true;
var _title = 'loading';
getTodoInfo() async {
_title = await toDoController
.getTodoInfo(
_todo[i]
);
_loading = false;
print(_title); // 'Clean!' <--- returns correct title
}
getTodoInfo();
return Container(
height: 150,
width: 150,
child: _loading
? Text(
_title,
)
: Text(
_title,
),
);
},
);
})
I am trying to make each Container calls the http requests to get the title from my database. Get the title and then update to the Text() widget below. However, it doesn't get updated after the value has been returned from the server.
I could make them wait for the request to get the title by using FutureBuilder. I tried with FutureBuilder too. However, FutureBuilder was not also reactive to the variable changes. So, I am trying to do this here. I kinda get the problem. After, the widget is returned, it is not changeable? Is there any way that I can do it with GetX?
Here's an example of using GetX with a Listview.builder.
This example uses a GetBuilder rather than Obx, as I'm not sure using a stream adds anything of benefit. If for some reason observables/streams are needed, numbers can be updated to be an .obs and the update() calls should be removed and GetBuilder replaced by GetX or Obx. If someone asks, I'll add that as an alternate example.
The GetBuilder wraps the ListView.builder and only the ListView will be rebuilt, not the entire widget tree / page.
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class ListDataX extends GetxController {
List<int> numbers = List<int>.from([0,1,2,3]);
void httpCall() async {
await Future.delayed(Duration(seconds: 1),
() => numbers.add(numbers.last + 1)
);
update();
}
void reset() {
numbers = numbers.sublist(0, 3);
update();
}
}
class GetXListviewPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
ListDataX dx = Get.put(ListDataX());
print('Page ** rebuilt');
return Scaffold(
body: SafeArea(
child: Column(
children: [
Expanded(
flex: 8,
child: GetBuilder<ListDataX>(
builder: (_dx) => ListView.builder(
itemCount: _dx.numbers.length,
itemBuilder: (context, index) {
return ListTile(
title: Text('Number: ${_dx.numbers[index]}'),
);
}),
),
),
Expanded(
flex: 1,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
RaisedButton(
child: Text('Http Request'),
onPressed: dx.httpCall,
),
RaisedButton(
child: Text('Reset'),
onPressed: dx.reset,
)
],
)
)
],
),
),
);
}
}
Obx / Streams version
Here's the above solution using Rx streams & Obx widget.
class ListDataX2 extends GetxController {
RxList<int> numbers = List<int>.from([0,1,2,3]).obs;
void httpCall() async {
await Future.delayed(Duration(seconds: 1),
() => numbers.add(numbers.last + 1)
);
//update();
}
void reset() {
numbers = numbers.sublist(0, 3);
//update();
}
}
class GetXListviewPage2 extends StatelessWidget {
#override
Widget build(BuildContext context) {
ListDataX2 dx = Get.put(ListDataX2());
print('Page ** rebuilt');
return Scaffold(
body: SafeArea(
child: Column(
children: [
Expanded(
flex: 8,
child: Obx(
() => ListView.builder(
itemCount: dx.numbers.length,
itemBuilder: (context, index) {
return ListTile(
title: Text('Number: ${dx.numbers[index]}'),
);
}),
),
),
Expanded(
flex: 1,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
RaisedButton(
child: Text('Http Request'),
onPressed: dx.httpCall,
),
RaisedButton(
child: Text('Reset'),
onPressed: dx.reset,
)
],
)
)
],
),
),
);
}
}
I've not tested it due to the fact that I don't have a complete sample but I think this is what you are looking for:
FutureBuilder<String>(
future: toDoController.getTodoInfo(_todo[i]),
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
if (snapshot.hasData) {
return Container(
height: 150,
width: 150,
child: Text(snapshot.data),
);
} else if (snapshot.hasError) {
return Text('Error');
} else {
return CircularProgressIndicator();
}
},
),
This is the code you need to return for every item of list builder.

Error in the code, BuildContext in Flutter

I am making an app with different build methods so that I can make a list of items to save them on another screen when the "love heart" button is tapped. But I am getting errors in the code. I am following the Flutter Codelabs app tutorial part 2.
My code:
import 'package:aioapp2/lists.dart';
import 'package:flutter/material.dart';
class _FavoriteListState extends State<FavoriteList> {
final _suggestions = [];
final Set<Widget> _saved = Set<Widget>();
Widget _buildList() {
return ListView.builder(
itemCount: 53,
itemBuilder: (BuildContext context, int index) {
return _buildRow(_suggestions[index]);
},
);
}
Widget _buildRow(Widget website){
final bool alreadySaved = _saved.contains(website);
return Card(
child: Container(
child: ListTile(
contentPadding: EdgeInsets.symmetric(horizontal: 20, vertical: 10),
subtitle: Row(
children: <Widget>[
// Image.asset('lib/images/${images[index]}'),
],
),
),
),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: _buildList(),
);
}
}
class FavoriteList extends StatefulWidget {
#override
_FavoriteListState createState() => _FavoriteListState();
}
The error that I'm facing is in the Image.asset() line. On typing the following line its showing red line under the "index". But it shouldn't and that's the problem! Any help?
You are attempting to reference the variable index from within the _buildRow method, but that variable doesn't exist there. Take a look at this excerpt from your code
Widget _buildList() {
return ListView.builder(
itemCount: 53,
itemBuilder: (BuildContext context, int index) { // <-- index declared here
return _buildRow(_suggestions[index]);
},
);
}
Widget _buildRow(Widget website){
final bool alreadySaved = _saved.contains(website);
return Card(
child: Container(
child: ListTile(
contentPadding: EdgeInsets.symmetric(horizontal: 20, vertical: 10),
subtitle: Row(
children: <Widget>[
// Image.asset('lib/images/${images[index]}'), // <-- index has not been declared here
],
),
),
),
);
}
This is an example of scope. The variable index is defined in the builder method within _buildList. That means the variable only exists there. You can't access it outside that method, so when you try to access it within _buildRow, you get an error.
If you want to pass the value of index to the _buildRow, you need to pass it as an argument to the _buildRow method:
Widget _buildList() {
return ListView.builder(
itemCount: 53,
itemBuilder: (BuildContext context, int index) { // <-- index declared here
return _buildRow(_suggestions[index], index); // Passing it as an argument
},
);
}
Widget _buildRow(Widget website, int index) {
final bool alreadySaved = _saved.contains(website);
return Card(
child: Container(
child: ListTile(
contentPadding: EdgeInsets.symmetric(horizontal: 20, vertical: 10),
subtitle: Row(
children: <Widget>[
Image.asset('lib/images/${images[index]}'), // <-- index has been declared in the parameter list so everything is ok here
],
),
),
),
);
}
(Your code doesn't declare images anywhere either, but I'm assuming you declare it elsewhere in code that you didn't share since you aren't reporting the error there.)
Try like this a simple workaround:
import 'package:flutter/material.dart';
void main() => runApp(
MaterialApp(
home: FavoriteList(),
)
);
class _FavoriteListState extends State<FavoriteList> {
final _suggestions = [{
'image' : 'ic_play.png'
},
{
'image' : 'ic_play.png'
},
{
'image' : 'ic_play.png'
},
{
'image' : 'ic_play.png'
}
];
final Set<Widget> _saved = Set<Widget>();
Widget _buildList() {
return ListView.builder(
itemCount: _suggestions.length,
itemBuilder: (BuildContext context, int index) {
return _buildRow(_suggestions[index]);
},
);
}
Widget _buildRow(dynamic website){
return Card(
child: Container(
child: ListTile(
contentPadding: EdgeInsets.symmetric(horizontal: 20, vertical: 10),
subtitle: Row(
children: <Widget>[
Image.asset('assets/images/${website['image']}'),
],
),
),
),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: _buildList(),
);
}
}
class FavoriteList extends StatefulWidget {
#override
_FavoriteListState createState() => _FavoriteListState();
}
Shouldnt
Image.asset('lib/images/${images[index]}'),
be like
Image.asset('lib/images/${index}.png'), // Provide you have 0.png, 1.png .... in lib folder
Or you should had an String array names images like
String[] images=["1.png","2.png"];
=====
I cant see any variable images which you want to reference in the commented code.