Flutter ListView doesn't update on setState - list

My flutter ListView doesn't update when my setState runs in my State class.
Yes, My main class is a stateful widget, incase anyone was wondering
class MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState() => new _MyAppState();
}
My setState function
setState(() {
if (price == "null") {
items.add("Item ${counter + 1}: error");
print("null");
} else {
items.add("Item ${counter + 1}: $price");
print("$price");
totalPrice += price;
}
counter++;
});
});
Before I placed my ListView within a Container -> Column -> Expanded it was working fine. But after I added it, it stopped updating when my setState ran
body: new Container(
child: Column(
children: <Widget>[
Expanded(
child: ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
final item = items[index];
return Dismissible(
key: Key(item),
onDismissed: (direction) {
setState(() {
items.removeAt(index);
counter--;
});
Scaffold.of(context).showSnackBar(
SnackBar(content: Text("$item deleted")));
},
background: Container(color: Colors.red),
child: ListTile(title: Text('$item')),
);
},
),
),
Could someone who is more knowledgeable in flutter educate me on what's going on here. I don't think adding the ListView into a container should make that much of a difference to how it works right?

Doing computations in setstate somehow caused this problem. Instead I did the computations in build since and I used setstate to only add to the list. It’ll then trigger the build and the rest of the things happen there. This solved my problem

Related

How can i make a dynamic list in Flutter where user can add items through a textfield and delete the item again?

I want to have a textinputfield for the user and when he tips in something and clicks on a button the input should be shown in a list item. The user should also have the option to delete items of the list just like a in and todo app.
Here you can find my code:
Link to Code
So I decided to write this simple program just to freshen up my skills... You can directly copy paste this code and it should work just fine.
I have used the provider package here to make this a bit more professional, as you can't always rely on setState() to update your UI when tasks are added to your list. And also because you will probably be using the provider more often in the future.
I have added comments in the below code to make it easy to understand. However, do not hesitate to clear up any confusions in the comments :)
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:todo/list_provider.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider( // This initiates the provider.
create: (context) => TaskProvider(), // Initiating it here makes this provider data available everywhere in the application
child: MaterialApp(
title: 'Flutter Demo',
home: const MyHomePage(),
),
);
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Dynamic List'),
),
body: const HomePageBody(), // I pass a separate widget here just to make the code a bit cleaner
floatingActionButton: FloatingActionButton(
onPressed: () => showModalBottomSheet( // This calls a bottom Modal Sheet which pops up while pressing the floating action button
context: context, builder: (context) => const BottomSheet()),// The modal sheet displays the BottomSheet() Widget which I have defined down in this code.
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
// This is where the ListView will be shown
class HomePageBody extends StatelessWidget {
const HomePageBody({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
List<String> listOfTasks = Provider.of<TaskProvider>(context).getTasks; // This is where the list is being accessed from the Provider file.
return Container(
padding: const EdgeInsets.all(20),
child: ListView.builder(
itemCount: listOfTasks.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(
listOfTasks[index],
),
);
},
),
);
}
}
// This is the BottomSheet Widget where I decided to take User Input from
class BottomSheet extends StatefulWidget {
const BottomSheet({Key? key}) : super(key: key);
#override
State<BottomSheet> createState() => _BottomSheetState();
}
class _BottomSheetState extends State<BottomSheet> {
String task = ''; // This variable holds the tasks user wants to add
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.only(
top: 20,
left: 20,
right: 20,
bottom: MediaQuery.of(context).viewInsets.bottom + 20, // viewInsets.bottom adds padding from the bottom to avoid keyboard overlapping textfield widget
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextFormField(// You can use TextField Widget as well
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
),
onChanged: (value) { // This saves the value in the TextField for every character the user types
task = value; // The value in the TextField is referred to by the 'value' variable
},
),
const SizedBox(
height: 10,
),
ElevatedButton(
onPressed: () => saveTask(task),
child: const Text('Save Task'),
),
],
),
);
}
void saveTask(String task) {
Provider.of<TaskProvider>(context, listen: false).addTasks(task); //This is where I am calling the function to add a task to the list.
// The 'addTasks()' function is defined in the provider file which is just below
}
}
Here's the list_provider.dart file that I import in above code:
import 'package:flutter/foundation.dart';
class TaskProvider extends ChangeNotifier { // This is the class where your data exists
// and this is the only place where your data should be manipulated! I explain the reason below...
final List<String> _tasks = [];
List<String> get getTasks { // We use a getter to retrieve the list
return _tasks; // We do that in order to avoid modifications to this list from any outside sources.
}
void addTasks(task) {
_tasks.add(task); // This is simply how you add anything to a list
notifyListeners(); // This is why we use providers. This function notifies all the children widgets
// of the Widget where we initiated our provider (see the parent of MaterialApp Widget in the above code)
// This is why changes to data should be made within this class only as it extends ChangeNotifier,
// which provides us with notifyListeners() method. Which ultimately notifies the widgets that the data has been modified and its time to rebuild the widgets that rely on this data!
}
}
You can copy paste this code, just make sure to add the provider package in your pubspec.yaml file as shown below.

How to display tasks that are not "checked" on the other screen?

I am looking at my code and wondering for 2 hours now without luck so I will ask for help here.
I have a button, when I press it, it displays a random item from the list view. The problem is I also have a check box on the list view with each item. I do not want it to (Shuffle through the items with the checkbox ticked) only to shuffle through the Task in the list view that are unchecked/unticked/are not done.
Here is my code
class TaskData extends ChangeNotifier {
List<Task> _tasks = [
Task(name: "item1"),
Task(name: "item2"),
Task(name: "item3"),
];
UnmodifiableListView<Task> get tasks {
return UnmodifiableListView(_tasks);
}
int get taskCount {
return _tasks.length;
}
// <<Here is the code that shuffles through list
Future<String> rann() async {
return (_tasks.toList()..shuffle()).first.name;
}
void addTask(String newTaskTitle) {
final task = Task(name: newTaskTitle);
_tasks.add(task);
notifyListeners();
}
void updateTask(Task task) {
task.toggleDone();
notifyListeners();
}
In another script I have this one
class Task {
final String name;
bool isDone;
Task({required this.name, this.isDone = false});
void toggleDone() {
isDone = !isDone;
}
}
In another script file I have this code
Padding(
padding:
const EdgeInsets.symmetric(horizontal: 20, vertical: 0),
child: FutureBuilder(
future: Provider.of<TaskData>(context).rann(),
builder: (context, snapshot) {
return Align(
alignment: Alignment.center,
child: Text(
"${snapshot.data}",
//softWrap: true,
textAlign: TextAlign.center,
//textWidthBasis: TextWidthBasis.longestLine,
style: TextStyle(
color: Colors.white,
fontSize: 30,
fontWeight: FontWeight.w700),
),
);
},
),
),
In another script I have this one
class TasksList extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Consumer<TaskData>(
builder: (context, taskData, child) {
return ListView.builder(
itemBuilder: (context, index) {
final task = taskData.tasks[index];
return TaskTile(
taskTitle: task.name,
isChecked: task.isDone,
checkboxCallback: (checkboxState) {
taskData.updateTask(task);
},
);
},
itemCount: taskData.taskCount,
);
},
);
}
}
Any help would be appreciated!
Edit : I also forgot to include this part of code
class TaskTile extends StatelessWidget {
final bool isChecked;
final String taskTitle;
final Function(bool?) checkboxCallback;
final VoidCallback longPressCallback;
TaskTile(
{required this.isChecked,
required this.taskTitle,
required this.checkboxCallback,
required this.longPressCallback});
#override
Widget build(BuildContext context) {
return ListTile(
onLongPress: longPressCallback,
title: Text(
taskTitle,
// at the bottom, it sets decoration of text if isChecked is true, if its not its null
style: TextStyle(
decoration: isChecked ? TextDecoration.lineThrough : null),
),
trailing: Checkbox(
activeColor: Colors.blue,
value: isChecked,
onChanged: checkboxCallback,
),
);
}
}
updated:
class TaskData extends ChangeNotifier {
List<Task> _undoneTasksShuffled = []
// you don't need anymore the rann method() instead you should iterate over this listView
UnmodifiableListView<Task> get undoneTasksShuffled => UnmodifiableListView<Task>(_undoneTasksShuffled);
#override
void notifyListeners() {
//this updates _undoneTasksShuffled every time you call notifyListeners
_undoneTasksShuffled = _tasks.where((e)=> !e.isDone).toList()..shuffle();
super.notifyListeners();
}
...
}
I think you only need to filter the results before get a random element. you need to modify your rann method for something like
//you don't really need a future method because you don't have async code
String rann() {
final r = Random();
final undoneTasks = _tasks.where((e)=> !e.isDone).toList();
//this is for avoid RangeException on list. you can return any other thing
if(undoneTasks.isEmpty) return '';
// i think that you don't really need to shuffle whole list, you only need a random element
return undoneTasks[r.nextInt(undoneTasks.length)].name;
}
i hope this solves your question

Flutter: getting switch toggle values from dynamic form or why does state change rebuild differs

I have kind of a form where I can add cards, each having 5 textfields and 2 switches. I would like to use a method to build the switch code (and the textfield code, but that is working). However, the switches refuse to show their intended state. I saw couple of similar questions. However, most were solved with a list view listing all switched/checkboxes next to one another (I have multiple cards with multiple textfields and multiple switches, each). This was close, but I don't really understand the answer (within the comments)
Actually some answers come up with the same (I guess more or less same because mine isn't working) code storing the switch state in a bool list. When debugging I can see that the values are correctly stored in the list. However, the changed value is not rendered upon state change.
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class MainPage extends StatefulWidget {
#override
_MainPageState createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
var descrTECs = <TextEditingController>[];
var fixedSCs = [true]; //storing the switch values
var cards = <Card>[]; // storing the list of cards with forms
SizedBox createTextField(String placeholderStr, double fieldWidth) {
var tFieldController = TextEditingController();
switch (placeholderStr) { //switching placeholder to assign text controller to correct controller list
case "Description":
descrTECs.add(tFieldController);
break;
}
return SizedBox(width: fieldWidth, height: 25,
child: CupertinoTextField(
placeholder: placeholderStr,
controller: tFieldController,
),
);
}
SizedBox createSwitch(int pos) {
return SizedBox(width: 50, height: 25,
child: CupertinoSwitch(
value: fixedSCs[pos],
onChanged: (value) {
setState(() => fixedSCs[pos] = value); // value is stored in fixedSCs but not rendered upon rebuild
},
)
);
}
Card createCard() {
return Card(
child: Row(children: <Widget>[
Text('#p${cards.length + 1}:'),
Column(
children: <Widget>[
createSwitch(cards.length),
createTextField("Description", 70.0),
],),
],),
);
}
#override
void initState() {
super.initState();
cards.add(createCard()); // first card created upon start
}
#override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
child: SafeArea(
child: Column(
children: <Widget>[
Expanded(
child: ListView.builder( // List Builder to show all cards
itemCount: cards.length,
itemBuilder: (BuildContext context, int index) {
return cards[index];
},
),
),
RaisedButton(
child: Text('add new'),
onPressed: () => setState(() {
fixedSCs.add(true); // bool element created to create next card
cards.add(createCard());} // create next card
),
),
],
),
),);
}
}
One thing I do not understand in general: Upon rebuild after a state change cards.length} should be my number of cards, let's say 3. And when it renders the 1st card, it passes the line Text("#p${cards.length + 1}"), so it should show #p3 and not #p1. What do I get wrong here?
I meanwhile got this working with quite some logic changes.
I put the switch builder into a stateless widget
class createSwitch extends StatelessWidget {
const createSwitch({
this.label, this.margin=const EdgeInsets.all(0.0), this.width, this.height, this.value, this.onChanged});
final String label; final EdgeInsets margin; final double width; final double height; final bool value;
final Function onChanged;
#override
Widget build(BuildContext context) {
return Container(
child: Row(
children: <Widget>[
Expanded(child: Text(label)),
CupertinoSwitch(
value: value,
onChanged: (bool newValue) {onChanged(newValue);},
),
],
),
),
} }
In the parent stateful controller I created a list to store the switches' state var parameterSCs = [true]; and each time I add a card I add a value whith clicking the button onPressed: () => setState(() {parameterSCs.add(true);}
I no longer store the cards widgets as a list. Instead, I build them directly in the code within a ListView.builder
ListView.builder(
itemCount: parameterSCs.length,
itemBuilder: (BuildContext context, int index) {
return Card( ...
In my real code I have 2 switches per card, so I always add 2 elements and the ListView count is then half of the parameterSCs' length.
I tried loads of approaches, this was the only one working

image_picker Cancel: Navigation.pop inside Widget builder for Flutter

I have the following issue in my Flutter App:
For the image_picker Cancel-button to work properly, I needed to be able to Navigate.pop() at the moment the user presses the Cancel-button inside the image_picker Plugin.
The main question for this image_picker-Cancel issue is: How can I navigate back (i.e. Navigator.pop(context)) inside a builder of a Widget ?
The following throws an error:
Widget _cancelBtnPressedWidget(BuildContext context) {
Navigator.pop(context);
}
I know a Widget is supposed to return something. Therefore is it possible to pseudo-return something - but actually keep the Navigator.pop() as the main action inside the Widget ??? (and best, automatically invoked without extra user-interaction)...
From the above code, the error sais:
flutter: ══╡ EXCEPTION CAUGHT BY ANIMATION LIBRARY ╞═════════════════════════════════════════════════════════
flutter: The following assertion was thrown while notifying status listeners for AnimationController:
flutter: setState() or markNeedsBuild() called during build.
flutter: This Overlay widget cannot be marked as needing to build because the framework is already in the
flutter: process of building widgets. A widget can be marked as needing to be built during the build phase
flutter: only if one of its ancestors is currently building. This exception is allowed because the framework
flutter: builds parent widgets before children, which means a dirty descendant will always be built.
flutter: Otherwise, the framework might not visit this widget during this build phase.
flutter: The widget on which setState() or markNeedsBuild() was called was:
flutter: Overlay-[LabeledGlobalKey<OverlayState>#b5c98](state: OverlayState#6a872(entries:
flutter: [OverlayEntry#cd1e7(opaque: false; maintainState: false), OverlayEntry#43b81(opaque: false;
flutter: maintainState: true), OverlayEntry#f0b49(opaque: false; maintainState: false),
flutter: OverlayEntry#b9362(opaque: false; maintainState: true)]))
flutter: The widget which was currently being built when the offending call was made was:
flutter: FutureBuilder<File>(dirty, state: _FutureBuilderState<File>#d3cac)
.
Here a more detailed description to where the above requirement comes from :
In fact, I would like to use the Navigator.pop() as soon as the user presses the cancel-Button as for a image_picker Plugin usage.
I realised that the snapshot.hashCode-change is one way to detect the Cancel-Button from being pressed by the user. Therefore if the user presses that Cancel-button, I would like to do no more than navigate.pop back to where I came from ;)... I don't want to show anymore or keep the user inside a Widget, but immediately return back to the view that originally Navigate.pushed.
Here is the image-picker part that does the image-finding - and the Cancel-treatment (i.e. call of _cancelBtnPressedWidget-Widget).
import 'package:flutter/material.dart';
import 'dart:io';
import 'package:image_picker/image_picker.dart';
File _imageFile;
bool _pickImage = true;
int _hashy = 0;
#override
Widget build(BuildContext context) {
if (_pickImage) {
return FutureBuilder<File>(
future: ImagePicker.pickImage(source: ImageSource.camera),
builder: (BuildContext context, AsyncSnapshot<File> snapshot) {
if (snapshot.hasData) {
_pickImage = false;
_imageFile = snapshot.data;
return _showImage(snapshot.data);
} else {
// when cancel is pressed, the hashCode changes...
if ((_hashy != 0) && (snapshot.hashCode != _hashy)) {
// when cancel pressed
return _cancelBtnPressedWidget(context);
}
_hashy = snapshot.hashCode;
return Scaffold(
body: Center(
child: Text('no image picker available'),
),
);
}
},
);
} else {
return _showImage(_imageFile);
}
}
Widget _cancelBtnPressedWidget(BuildContext context) {
// requires a return ..... How to overcome this requirement ????
Navigator.pop(context);
}
Widget _showImage(File imgFile) {
return Scaffold(
body: SafeArea(
child: Stack(
alignment: AlignmentDirectional.topStart,
children: <Widget>[
Positioned(
left: 0.0,
bottom: 0.0,
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Center(
child: imgFile == null
? Text('No image selected.')
: Image.file(imgFile),
),
),
// more stacks ... not important here....
],
),
),
);
}
Of course, inside pubspec.yaml you add the necessary dependency:
dependencies:
flutter:
sdk: flutter
image_picker: ^0.5.0+3
Add-on:
I tried to add a confirmation-dialog (i.e. to ask the user "Do you really want to cancel").
Now, the above error is gone. However, now the image_picker keeps popping up again and again...overwriting this dialog.
What am I still doing wrong her ??
Widget _cancelBtnPressedWidget(BuildContext context) {
return AlertDialog(
title: Text('Camera Alert'),
content: Text('Are you sure you want to cancel ?'),
actions: <Widget>[
FlatButton(
child: Text('Close'),
onPressed: () {
Navigator.pop(context);
},
)
],
);
}
I finally found an answer:
Indeed, I was able to place a confirmation-dialog and there I was able to place the necessary return Widget.
Now the Cancel for the image_picker is working as expected !
Here is the entire Code:
import 'package:flutter/material.dart';
import 'dart:io';
import 'package:image_picker/image_picker.dart';
class MyImagePickerView extends StatefulWidget {
_MyImagePickerViewState createState() => _MyImagePickerViewState();
}
class _MyImagePickerViewState extends State<MyImagePickerView> {
File _imageFile;
bool _pickImage = true;
int _hashy = 0;
bool _cancelPressed = false;
#override
Widget build(BuildContext context) {
if (_pickImage) {
return FutureBuilder<File>(
future: ImagePicker.pickImage(source: ImageSource.camera),
builder: (BuildContext context, AsyncSnapshot<File> snapshot) {
if (snapshot.hasData) {
_pickImage = false;
_imageFile = snapshot.data;
return _showImage(snapshot.data);
} else {
// when cancel is pressed, the hashCode changes...
if ((_hashy != 0) && (snapshot.hashCode != _hashy)) {
// when cancel pressed
return _cancelBtnPressedWidget(context);
}
_hashy = snapshot.hashCode;
return Scaffold(
body: Center(
child: Text('no image picker available'),
),
);
}
},
);
} else {
if (_cancelPressed) {
return _showAlert();
} else {
return _showImage(_imageFile);
}
}
}
Widget _cancelBtnPressedWidget(BuildContext context) {
_cancelPressed = true;
_pickImage = false;
return Scaffold(
body: Center(
child: Text('Press button to start.'),
),
);
}
Widget _showImage(File imgFile) {
StateContainerState container = StateContainer.of(context);
return Scaffold(
body: SafeArea(
child: Stack(
alignment: AlignmentDirectional.topStart,
children: <Widget>[
Positioned(
left: 0.0,
bottom: 0.0,
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Center(
child: imgFile == null
? Text('No image selected.')
: Image.file(imgFile),
),
),
// more stacks ... not important here....
],
),
),
);
}
Widget _showAlert() {
return AlertDialog(
title: Text('Camera Alert'),
content: Text('Are you sure you want to cancel the Camera ?'),
actions: <Widget>[
FlatButton(
child: Text('No'),
onPressed: () {
setState(() {
_pickImage = true;
_cancelPressed = false;
});
},
),
FlatButton(
child: Text('Yes'),
onPressed: () {
Navigator.pop(context);
},
),
],
);
}
#override
void dispose() {
_myController.dispose();
super.dispose();
}
}
To me it doesn't seem like you are capturing the click at all. For me I would return a button in the _cancelBtnPressedWidget and in the onPressed call the pop.

Infinite List in Flutter Application

I am migrating my application from android to flutter and till now I have used ListView in a flutter. my question is, is there any specialized technique to handle a large amount of data in the flutter? for reference, you can look at android RecyclerView. it handles in-memory views and recycles its runtime. so how to achieve functionality like RecyclerView in Flutter? or it's not necessary for the flutter?
The easiest way is to use a ListView.builder without specifying the itemCount parameter.
Here is the simplest example:
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: new MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("Infinite List"),
),
body: ListView.builder(
itemBuilder: (context, index) {
return Text("$index");
},
),
);
}
}
Later, you can enhance this by fetching real data. You could show a 'CircularProgressIndicator' in the last item of the list while waiting for the new data.
body: ListView.builder(
itemBuilder: (context, index) {
if (index < data.length) {
// Show your info
return Text("$index");
} else {
getMoreData();
return Center(child: CircularProgressIndicator());
}
},
itemCount: data.length + 1,
),
You can see that we trick the list by adding an index, and calling for more data when displaying that final index.
getMoreData() would include a call to setState() to force a rebuild and to take into account the new data.
Below is a simple infinite list widget based on chemamolins's answer. It accepts an itemBuilder to build the current item and onRequest callback to request more data when the user scrolls to the bottom.
import 'package:flutter/material.dart';
typedef Future<List<T>> RequestFn<T>(int nextIndex);
typedef Widget ItemBuilder<T>(BuildContext context, T item, int index);
class InifiniteList<T> extends StatefulWidget {
final RequestFn<T> onRequest;
final ItemBuilder<T> itemBuilder;
const InifiniteList(
{Key? key, required this.onRequest, required this.itemBuilder})
: super(key: key);
#override
_InifiniteListState<T> createState() => _InifiniteListState<T>();
}
class _InifiniteListState<T> extends State<InifiniteList<T>> {
List<T> items = [];
bool end = false;
_getMoreItems() async {
final moreItems = await widget.onRequest(items.length);
if (!mounted) return;
if (moreItems.isEmpty) {
setState(() => end = true);
return;
}
setState(() => items = [...items, ...moreItems]);
}
#override
Widget build(BuildContext context) {
return ListView.builder(
itemBuilder: (context, index) {
if (index < items.length) {
return widget.itemBuilder(context, items[index], index);
} else if (index == items.length && end) {
return const Center(child: Text('End of list'));
} else {
_getMoreItems();
return const SizedBox(
height: 80,
child: Center(child: CircularProgressIndicator()),
);
}
},
itemCount: items.length + 1,
);
}
}
Usage
child: InifiniteList<String>(
onRequest: requestItems,
itemBuilder: (context, item, index) => Container(
padding: const EdgeInsets.all(30),
color: index % 2 == 0 ? Colors.purple.shade100 : Colors.lime.shade100,
child: Text(item, style: Theme.of(context).textTheme.headline6),
),
),
// normally this is the place where you request the next batch of items
// on the network.
Future<List<String>> requestItems(int nextIndex) {
const pageSize = 15;
var result = List<String>.generate(pageSize, (i) => "Item: ${nextIndex + i + 1}");
return Future<List<String>>.delayed(
const Duration(milliseconds: 500),
() => result,
);
}
Live Demo
Displaying lists of data is a fundamental pattern for mobile apps. Flutter includes the ListView widget to make working with lists a breeze.
I have solved the issue by doing the following steps
Use the ListView Widget
There are four constructors of ListView Class
You have to use Builder Constructor (ListView.builder)
Builder Constructor is used when you have to make a list of elements on demand
It is appropriate for list views with a large (or infinite) number of children
HERE you can have Solution Video CLICK HERE