i want to add a row in listview.builder but it goes blank - flutter

i want to add a row ( list of buttons that do filter the list), i tried wrapping listTile in column, listview.builde in column but it doesn't work. tried wrapping GetBuilder also but it doesn't work.
enter image description here
My Code :-
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:quizzy/data_controller.dart';
import '../models/showQuestion.dart';
class AllQuestionBank extends StatefulWidget {
const AllQuestionBank({Key? key}) : super(key: key);
#override
State<AllQuestionBank> createState() => _AllQuestionBankState();
}
class _AllQuestionBankState extends State<AllQuestionBank> {
final DataController controller = Get.put(DataController());
#override
Widget build(BuildContext context) {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
controller.getQuestionList();
});
return Scaffold(
appBar: AppBar(
title: const Text(' Question Bank'),
),
body: GetBuilder<DataController>(
builder: (controller) => controller.QuestionList.isEmpty
? const Center(
child: Text('😔 NO DATA FOUND (: 😔'),
)
: ListView.builder(
itemCount: controller.QuestionList.length,
itemBuilder: (context, index) {
return ListTile(
title: showQuestion(controller.QuestionList[index]),
);
}),
),
);
}
}

You could redefine your ListView as:
ListView.builder(
itemCount: controller.QuestionList.length + 1,
itemBuilder: (context, index) {
if (index == 0) {
return WhateverRowYouWant();
}
return ListTile(
title: showQuestion(controller.QuestionList[index - 1]),
);
}),

You can just define the scroll direction to be horizontal.
ListView.builder(
itemCount: controller.QuestionList.length,
scrollDirection: Axis.horizontal, <- added this line
itemBuilder: (context, index) {
return ListTile(
title: showQuestion(controller.QuestionList[index]),
);
}),
You can also find an example from the official docs here

Try this.
physics: NeverScrollableScrollPhysics(), shrinkWrap: true,

To add Row() on top you need Column() widget for sure
After that, you have to wrap ListView.builder() with the Expanded() widget this will help you
Ex.
return Scaffold(
body: SafeArea(
child: Column(
children: [
Row(
children: [
TextButton(
onPressed: () {},
child: Text('Filter'),
),
],
),
Expanded(
child: GetBuilder<DataController>(
builder: (controller) => controller.QuestionList.isEmpty
? const Center(
child: Text('😔 NO DATA FOUND (: 😔'),
)
: ListView.builder(
shrinkWrap: true,
itemCount: controller.QuestionList.length,
itemBuilder: (context, index) {
return ListTile(
title: showQuestion(controller.QuestionList[index]),
);
},
),
),
),
],
),
),
);
Please, try this!!

This is the combination of GetxController and ListView with a top row I use:
class MyController extends GetxController {
var isRunning = true.obs; // set to isRunning.value = false; if done loading QuestionList
RxList<ProductModel> QuestionList = <ProductModel>[].obs;
}
Obx( () => controller.isRunning.isTrue
? 'Loading'
: ListView.builder(
itemCount: controller.QuestionList.length + 1,
itemBuilder: (BuildContext context, int index) {
if (index == 0) {
return Text('TOP ROW');
}
return ListTile(
title: showQuestion(controller.QuestionList[index - 1]),
);
}),
);

Related

flutter/dart: streambuilder inside list view not scrolling

Is there anyway to making a streambuilder inside a list view scrollable?
I tried wrapping the form on a column widget but its not working out. none of the information is displayed on the screen for some reason.
here is a mini video of the issue:: https://youtu.be/_GIZkwzeH0Y
is there a different way to display this information?
return Scaffold(
appBar: AppBar(
title: const Text('Job details'),
),
body: FutureBuilder(
future: createOrGetExistingJob(context),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.done:
_setupTextControllerListener();
// at this point we want to start to listening for changes
return Form(
key: _formKey,
child: ListView(
padding: const EdgeInsets.all(32.0),
children: [
getStateChevrons(_jobState),
const Divider(
height: 20,
thickness: 5,
indent: 0,
endIndent: 0,
color: Colors.blue,
),
//
//
//
// Below is the streambuilder I would like to correctly display
getStateDependentButtons(context),
_jobDocumentId != null && _jobState != jobStateNew
? StreamBuilder(
stream: _jobsService
.getJobApplication(_jobDocumentId as String),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
case ConnectionState.active:
if (snapshot.hasData) {
final allJobApplications = snapshot.data
as Iterable<CloudJobApplication>;
return JobApplicationsListView(
jobApplications: allJobApplications,
onTap: (job) {
Navigator.of(context).pushNamed(
myJobApplicationsRoute,
arguments: job,
);
},
);
} else {
return const CircularProgressIndicator();
}
default:
return const CircularProgressIndicator();
}
},
)
: const Text(
'Once job is submitted, job applications will be available')
]
.map((child) => Padding(
padding: EdgeInsets.symmetric(vertical: 8.0),
child: child,
))
.toList(),
),
);
default:
return const CircularProgressIndicator();
}
},
),
//
//
);
Here is what jobApplicationListView looks like::
import 'package:flutter/material.dart';
import '../../services/cloud/cloud_job_application.dart';
import '/services/cloud/cloud_job.dart';
import '/utilities/dialogs/delete_dialog.dart';
/*
source: https://www.youtube.com/watch?v=VPvVD8t02U8&t=59608s
class creation :: 22:02:54
*/
typedef JobCallback = void Function(CloudJobApplication jobApplication);
class JobApplicationsListView extends StatelessWidget {
final Iterable<CloudJobApplication>
jobApplications; // list of jobApplications
//final JobCallback onDeleteJob;
final JobCallback onTap;
const JobApplicationsListView({
Key? key,
required this.jobApplications,
//required this.onDeleteJob,
required this.onTap,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return ListView.builder(
shrinkWrap: true,
itemCount: jobApplications.length,
itemBuilder: (context, index) {
final jobApplication = jobApplications.elementAt(index);
return ListTile(
onTap: () {
onTap(jobApplication);
},
title: Text(
jobApplication.jobApplicationDescription,
maxLines: 1,
softWrap: true,
overflow: TextOverflow.ellipsis,
),
trailing: IconButton(
onPressed: () async {
final shouldDelete = await showDeleteDialog(context);
if (shouldDelete) {
"onDeleteJob(job)";
}
},
icon: const Icon(Icons.delete),
),
);
},
);
}
}
On your JobApplicationsListView's ListView.builder( set physics: NeverScrollableScrollPhysics(), The parent already handling scroll event.
#override
Widget build(BuildContext context) {
return ListView.builder(
physics: NeverScrollableScrollPhysics(),
itemCount: jobApplications.length,
itemBuilder: (context, index) {
final jobApplication = jobApplications.elementAt(index);
Also you can try with primary: false, on it.
Or you can use Column widget instead.

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]);
}),
],
);
}),
);
}
}),
],
),
),

Flutter pageview, add new elements at runtime

In my project I have a Pageview which contains widgets from a list. In runtime I add more elements to this list to show it in the Pageview. If I don't specify itemCounter than I can run out of index but I can see the new pages, however, if use itemCounter, the new page will not appear. For the itemCounter I use the length of my list. How can I add new elements runtime to a pageview?
In this case I can add new elements, but I can run out of index:
child: PageView.builder(
controller: pageController,
//itemCount: _dummy.length,
itemBuilder: (context, position) {
return _dummy[position];
},
),
),
This is case the new pages doesn't even show up. It's like the itemCounter doesn't change.
child: PageView.builder(
controller: pageController,
itemCount: _dummy.length,
itemBuilder: (context, position) {
return _dummy[position];
},
),
),
in this widget, when you clickfab it will create another page in PageView.
import 'package:flutter/material.dart';
class PageViewOnRuntime extends StatefulWidget {
PageViewOnRuntime({Key? key}) : super(key: key);
#override
_PageViewOnRuntimeState createState() => _PageViewOnRuntimeState();
}
class _PageViewOnRuntimeState extends State<PageViewOnRuntime> {
final PageController pageController = PageController();
int numberOfPage = 2;
#override
Widget build(BuildContext context) {
return Scaffold(
body: PageView.builder(
controller: pageController,
itemCount: numberOfPage,
itemBuilder: (context, index) {
return Container(
color: index % 2 == 0 ? Colors.cyanAccent : Colors.blueGrey,
alignment: Alignment.center,
child: Text(index.toString()),
);
},
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
numberOfPage++;
});
},
child: Icon(Icons.add),
),
);
}
}
let me know , if you need something else .

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.

Dynamic ListView height inside another ListView

I need to make a dynamic ListView height inside another ListView. None of the answers here I have come to didn't really answer it. I've made a simple example of what I'm trying to do so you can simply copy and paste it to try it and play with it. I've got problem with sub ListView where I need to make it grow or shrink based on number of items in it (problem commented in program)
class Test extends StatefulWidget {
#override
_TestState createState() => _TestState();
}
List<List<bool>> subList = [
[true, true],
[true]
];
class _TestState extends State<Test> {
#override
Widget build(BuildContext context) {
return ListView.separated(
itemBuilder: (BuildContext context, int index) {
return Column(
children: <Widget>[
ListTile(
trailing: IconButton(
icon: Icon(Icons.add),
onPressed: () {
setState(() {
subList[index].add(true);
});
},
),
title: Text(
'item $index',
style: TextStyle(fontWeight: FontWeight.w600),
),
),
Container(
height: 100, // <--- this needs to be dynamic
child: ListView.builder(
physics: NeverScrollableScrollPhysics(),
itemBuilder: (BuildContext context, int subIndex) {
return TestRow(
text: 'sub$subIndex',
onRemove: () {
setState(() {
subList[index].removeAt(subIndex);
});
});
},
itemCount: subList[index].length,
),
)
],
);
},
separatorBuilder: (BuildContext context, int index) => Divider(),
itemCount: subList.length);
}
}
class TestRow extends StatelessWidget {
final String text;
final Function onRemove;
const TestRow({this.onRemove, this.text});
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(text),
IconButton(
icon: Icon(Icons.remove),
onPressed: onRemove,
)
],
),
);
}
}
BTW I managed to make a workaround by changing height of container (commented part) to height: 50.0 * subList[index].length where 50 is height of sub title. I'm still looking for a proper way of doing it where I wouldn't need to hardcode height of the tile and calculate it
Here is video of the project with workaround how it should work
Try setting the shrinkWrap property to true and remove the container
ListView.builder(
shrinkWrap: true, //<-- Here
physics: NeverScrollableScrollPhysics(),
itemBuilder: (BuildContext context, int subIndex) {
return TestRow(
text: 'sub$subIndex',
onRemove: () {
setState(() {
subList[index].removeAt(subIndex);
});
});
},
itemCount: subList[index].length,
)
Output: