I have listview with checkboxtile filled with data in which when i will select option it will checkmark that item. Listview meant to be multi select option. There is model class for converting jsonresponse to data. UI code is working fine but ontap action causing bug in application.
When on tap item selected it check the box and reset spontaneously. Issue is regarding selecting the checkbox on all item it reset automatically. It's look like something wrong with model class. Facing issue on emulator as well as physical device.
Appreciate your time.
This is my CommonResponse class
class CommonResponse {
final String id;
final String name;
bool isSelected = false;
CommonResponse({this.id, this.name});
factory CommonResponse.fromJson(Map<String, dynamic> json) {
return CommonResponse(
id: json['id'],
name: json['name'] as String,
);
}
}
This is UI code:
import 'dart:convert';
import 'package:flutter/material.dart';
class WorkList extends StatefulWidget {
Key key;
WorkList({this.key}) : super(key: key);
#override
_WorkListState createState() => _WorkListState();
}
class _WorkListState extends State<WorkList> {
Constants constants = Constants();
List<String> selectedPrefIdList = List();
Future<List<CommonResponse>> _getList() async {
var jsonData =
'[{"id": "1", "name": "1-2 day"}, {"id": "2", "name": "cameo"}, {"id": "5", "name": "movies"}, {"id": "6", "name": "ads"}]';
List jsonResponse = json.decode(jsonData);
var workPrefOutput =
jsonResponse.map((list) => CommonResponse.fromJson(list)).toList();
return workPrefOutput;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Container(
child: FutureBuilder<List<CommonResponse>>(
future: _getList(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.separated(
separatorBuilder: (context, index) {
return Divider();
},
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return CheckboxListTile(
title: Text(snapshot.data[index].name),
controlAffinity: ListTileControlAffinity.platform,
value: snapshot.data[index].isSelected,
onChanged: (value) {
setState(() {
snapshot.data[index].isSelected = value;
var selectedId = snapshot.data[index].id;
selectedPrefIdList.contains(selectedId)
? selectedPrefIdList.remove(selectedId)
: selectedPrefIdList.add(selectedId);
// print(selectedPrefIdList);
});
},
);
});
} else {
return Center(
child: CircularProgressIndicator(),
);
}
},
),
),
),
);
}
}
I guess setstate is creating a problem. It is reloading future builder again.
Because of this your selection is resetting
Im not sure if this will work, but maybe you can try switching this
snapshot.data[index].isSelected = value;
to this
snapshot.data[index].isSelected = !snapshot.data[index].isSelected;
Related
I have found in this link a way to place data from JSON dynamically to DataTable() widget:
Dynamically display JSON data in data table layout in flutter
In that post he has ColumnWidths in the json but he doesn't show how he changes width of the individual datacell columns.
I have used that example for myself as well and I wonder how I can use the column widths from json as well to change width of individual data cells?
This is the json I am using:
[
{
"table-data": [
{
"table-label-data": "SL.´Customer´Balance Qty´Amount´Oldest / Recent ",
"table-row-list": [
{
"table-row-data": "1. ´ABD ´14 / 14.60´11,090´313 / 313"
},
{
"table-row-data": "1. ´ABD ´14 / 14.60´11,090´313 / 313"
}
],
"table-cell-widths": "40´168´96´96´108"
}
]
}
]
This is the model I am using:
import 'dart:ui';
class TableModel {
TableModel(this.labelData, this.rowData);
List<String> labelData;
List<List<String>> rowData;
factory TableModel.fromJson(Map<String, dynamic> json) {
return TableModel(
json['table-data'][0]["table-label-data"].split('´').toList(),
buildRowData(json),
);
}
}
List<List<String>> buildRowData(Map<String, dynamic> json) {
List<List<String>> rowDataCollection = [];
json['table-data'][0]["table-row-list"].forEach((rows) {
rowDataCollection.add(rows['table-row-data'].split('´').toList());
});
return rowDataCollection;
}
For the view as you can see i use a variable width inside SizedBox widget to change width, but it is now changing all widths but i want to change individual data cell widths based on json.
This is the view:
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:test_project/controllers/getters/get_test.dart';
import '../models/table_model.dart';
import '../models/test_model.dart';
class AppView2 extends StatefulWidget {
const AppView2({
Key? key,
}) : super(key: key);
#override
_AppViewState createState() => _AppViewState();
}
class _AppViewState extends State<AppView2> {
Future<void> generateList() async {
String responseBody =
await rootBundle.loadString("assets/tableJsonData.json");
var list = await json.decode(responseBody).cast<Map<String, dynamic>>();
return await list
.map<TableModel>((json) => TableModel.fromJson(json))
.toList();
}
#override
void initState() {
generateList();
super.initState();
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
appBar: AppBar(
title: Text('DataTable'),
),
body: FutureBuilder(
future: generateList(),
builder: (context, AsyncSnapshot snapShot) {
if (snapShot.data == null ||
snapShot.connectionState == ConnectionState.waiting ||
snapShot.hasError ||
snapShot.data.length == 0) {
return Container(
child: Center(child: CircularProgressIndicator()),
);
} else {
return ListView.builder(
shrinkWrap: true,
scrollDirection: Axis.vertical,
itemCount: snapShot.data.length,
itemBuilder: (BuildContext context, int index) {
final TableModel table = snapShot.data[index];
return SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: DataTable(
columns: table.labelData.map<DataColumn>((e) {
var columnName = e;
return DataColumn(
label: Text(
columnName,
));
}).toList(),
rows: table.rowData.map<DataRow>((e) {
return DataRow(
cells: e.map<DataCell>((e) {
var dataCell = e;
dynamic width;
return DataCell(SizedBox(
width: width,
child: Text(
dataCell,
),
));
}).toList());
}).toList(),
),
);
});
}
},
)));
}
}
I have checkbox listtile inside listview builder I want when I check any one then its data add to list then when I uncheck it be removed from list:
Directionality(
textDirection: TextDirection.rtl,
child: ListView.builder(
itemCount: student.length,
itemBuilder: (context, index) {
return Card(
child: CheckBoxedListTile(
student[index], widget.date, widget.time,widget.teacher,widget.subject));
}),
),
);
}
}
check listtile widget is :
class _CheckBoxedListTileState extends State<CheckBoxedListTile> {
var checked;
#override
void initState() {
checked = false;
super.initState();
}
#override
Widget build(BuildContext context) {
return Consumer<AbsenceProvider>(builder: (context, absProv, child) {
return CheckboxListTile(
value: checked,
onChanged: (val) {
setState(() {
checked = !checked;
});
var data = {
"name": widget.student.name,
"stage": widget.student.stage,
"group": widget.student.group,
"teacher": widget.teacher,
"subject": widget.subject,
"date": widget.date,
"time": widget.time,
"vacs": "No"
};
if (checked == true) {
absProv.addAbs(data);
} else {
absProv.remAbs(data);
}
print(absProv.absences);
},
title: Text('${widget.student.name}'),
);
});
}
}
provider is :
class AbsenceProvider with ChangeNotifier {
var absences = [];
addAbs(item) {
absences.add(item);
notifyListeners();
}
remAbs(item) {
absences.remove(item);
notifyListeners();
}
}
when I click on check box it is add successfully
but when i click again it is nor remove it
I solved it by using removeWhere :
before :
absences.remove(item);
after :
absences.removeWhere((e) => e['name'] == item['name']);
This my Code. When i run code i'm getting a error like this
"TypeError (type 'String' is not a subtype of type 'int' of 'index')".
Is it something Do with Json Data or the I'm using Wrong Data Type.
Does Future<List<Arrivals>> is written correctly?
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<Arrivals> details = [];
Future<List<Arrivals>> _getDetails() async {
var data =
await http.get("https://flight-api-maldives.herokuapp.com/arrivals");
var jsonData = jsonDecode(data.body);
for (var val in jsonData) {
Arrivals arrivals = Arrivals(
val['Scheduled'],
val['Revised'],
val['From'],
val['Flight'],
val['Status'],
);
details.add(arrivals);
print(details.length);
}
return details;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
child: Column(
children: <Widget>[
FutureBuilder(
future: _getDetails(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.data != null) {
return Container(
child: ListView.builder(
itemBuilder: (BuildContext context, int index) {
return Center(child: Text('snapshot.data[index].Scheduled'),);
}
),
);
}else{
return Center(
child: Text('NO'),
);
}
},
)
],
),
),
);
}
}
class Arrivals {
final String Scheduled;
final String Revised;
final String From;
final String Flight;
final String Status;
Arrivals(this.Scheduled, this.Revised, this.From, this.Flight, this.Status);
}
this is the json data im using:
[
[
{
"Scheduled": "06:35",
"Revised": "06:35",
"From": "Kaadedhdhoo (KDM)",
"Flight": "Maldivian Q2 149",
"Status": "On-Time"
},
{
"Scheduled": "06:40",
"Revised": "06:40",
"From": "Dharavandhoo Island (DRV)",
"Flight": "Maldivian Q2 289",
"Status": "On-Time"
},
]
]
Where is the picture of my error
https://i.stack.imgur.com/qVWLc.png
the main problem is in json. it is list of list.
var jsonData = jsonDecode(data.body);
jsonData = jsonData[0]; // added line
for (var val in jsonData) {
Moreover their is no need of so many extra code to just display list view you can use following simple code to display list view. In addition to that Listview.builder require item count property to specify total number of item.
Replace below build method with your will work for you.
#override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder(
future: _getDetails(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
print(snapshot);
if (snapshot.data != null) {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return Center(
child: Text(snapshot.data[index].Scheduled.toString()),
);
});
} else {
return Center(
child: Text('NO'),
);
}
},
),
);
}
In the JSON data, you've got a list inside a list.
val['Scheduled'],
This line is getting called on a list, so the square brackets are expecting an index rather than a map key
This is my list view widget. There are two list view builders, one inside another. I added shrinkWrap property and physics property. Nothing is rendered.I have another doubt when to use list view, single child scroll view and custom scroll view.
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: AppBar(
title: Text("Listviews"),
backgroundColor: Colors.blue,
),
body: ListView.builder(
shrinkWrap: true,
itemCount: data == null ? 0 : data.length,
itemBuilder: (BuildContext context, int index) {
if (data[index]["type"] == "single") {
var innerData = data[index]["data"];
return Container(
child: ListView.builder(
shrinkWrap: true,
itemCount: innerData == null ? 0 : innerData.length,
itemBuilder: (BuildContext context, int index) {
String title = innerData[index]["title"];
return Text("$title");
},
),
);
}
},
),
);
}
This is the output screen
This is my json response:
[
{
"type": "single",
"data": [
{
"title": "Fresh Vegetables"
},
{
"title": "Fresh Fruits"
},
{
"title": "Cuts and Sprouts"
},
{
"title": "Exotic Center"
}
]
}
]
I want to do like the flipkart home page. I want to build widgets based on the response. What is the widgets should I use?
Use physics property inside listViewBuilder
shrinkWrap: true,
physics: ClampingScrollPhysics(), /// listView scrolls
I some how copy pasted your code and made some modifications and it worked for me just check the code i have modified :
I have loaded your json locally mentioned below:
[
{
"type": "single",
"data": [
{
"title": "Fresh Vegetables"
},
{
"title": "Fresh Fruits"
},
{
"title": "Cuts and Sprouts"
},
{
"title": "Exotic Center"
}
]
}
]
According to you json class i have created a model class where you can access the specific object from the listview using this model class :
// To parse this JSON data, do
//
// final data = dataFromJson(jsonString);
import 'dart:convert';
List<Data> dataFromJson(String str) => List<Data>.from(json.decode(str).map((x) => Data.fromJson(x)));
String dataToJson(List<Data> data) => json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class Data {
String type;
List<Datum> data;
Data({
this.type,
this.data,
});
factory Data.fromJson(Map<String, dynamic> json) => Data(
type: json["type"],
data: List<Datum>.from(json["data"].map((x) => Datum.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"type": type,
"data": List<dynamic>.from(data.map((x) => x.toJson())),
};
}
class Datum {
String title;
Datum({
this.title,
});
factory Datum.fromJson(Map<String, dynamic> json) => Datum(
title: json["title"],
);
Map<String, dynamic> toJson() => {
"title": title,
};
}
And just check the main file where i have made the changes :
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:sample_testing_project/models.dart';
main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
List<Data> data = List();
bool _isLoading = false;
#override
void initState() {
// TODO: implement initState
super.initState();
loadYourData();
}
Future<String> loadFromAssets() async {
return await rootBundle.loadString('json/parse.json');
}
loadYourData() async {
setState(() {
_isLoading = true;
});
// Loading your json locally you can make an api call, when you get the response just pass it to the productListFromJson method
String jsonString = await loadFromAssets();
final datamodel = dataFromJson(jsonString);
data = datamodel;
setState(() {
_isLoading = false;
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: new Scaffold(
appBar: AppBar(
title: Text("Listviews"),
backgroundColor: Colors.blue,
),
body: ListView.builder(
shrinkWrap: true,
itemCount: data == null ? 0 : data.length,
itemBuilder: (BuildContext context, int index) {
if (data[index].type == "single") {
var innerData = data[index].data;
return Container(
child: ListView.builder(
shrinkWrap: true,
itemCount: innerData == null ? 0 : innerData.length,
itemBuilder: (BuildContext context, int index) {
String title = innerData[index].title;
return Container(
width: MediaQuery.of(context).size.width,
child: Card(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text("$title"),
),
),
);
},
),
);
}
},
),
),
);
}
}
i want to make my LIstTile like checkbox, but the problem is when i click one of them, all of the ofthem is selected.
children: <Widget>[
new Expanded(
child:FutureBuilder<List<Workers>>(
future: fetchWorkers(http.Client()),
builder: (context, snapshot) {
if (snapshot.hasError) print(snapshot.error);
return snapshot.hasData
? WorkerList(workers: snapshot.data)
: Center(child: CircularProgressIndicator());
},
),),
and here is how i get the value from json and show it to my ListTile
Future<List<Workers>> fetchWorkers(http.Client client) async {
final response = await http.post(app_configuration.getUrl() + 'api/Worker/getAll/');
return compute(parseWorkers, response.body);
}
static List<Workers> parseWorkers(String responseBody) {
final parsed = json.decode(responseBody).cast<Map<String, dynamic>>();
return parsed.map<Workers>((json) => Workers.fromJson(json)).toList();
}
and Here is my Workers
class Workers {
final String UserId;
final String Fullname;
Workers(
{
this.UserId,
this.Fullname
});
factory Workers.fromJson(Map<String, dynamic> json) {
return Workers(
UserId: json['UserId'] as String,
Fullname: json['Fullname'] as String,
);
}
}
class WorkerList extends StatefulWidget {
#override
_WorkerListState createState() => new _WorkerListState();
final List<Workers> workers;
WorkerList({Key key, #required this.workers}) : super(key: key);
}
class _WorkerListState extends State<WorkerList> {
var isSelected = false;
var mycolor=Colors.white;
#override
Widget build(BuildContext context) {
return ListView.builder(
physics: ClampingScrollPhysics(),
shrinkWrap: true,
itemCount: widget.workers.length,
itemBuilder: (context, index) {
return Column(
children: <Widget>[
Card(
child: new ListTile(
selected: isSelected,
leading: const Icon(Icons.info),
title: new Text(widget.workers[index].Fullname),
subtitle: new Text(widget.workers[index].UserId),
onTap: toggleSelection // what should I put here,
),),
],
);
},
);
}
void toggleSelection() {
setState(() {
if (isSelected) {
mycolor=Colors.blue;
isSelected = false;
} else {
mycolor=Colors.grey[300];
isSelected = true;
}
});
}
}
Here is the screenshot
How can i fix it ? did i miss something ?
you can do the following:
add a bool inside your Workers class
class Workers {
final String UserId;
final String Fullname;
bool isSelected=false;//the initializtion is mandatory
Workers(
{
this.UserId,
this.Fullname
});
factory Workers.fromJson(Map<String, dynamic> json) {
return Workers(
UserId: json['UserId'] as String,
Fullname: json['Fullname'] as String,
);
}
}
and in the _WorkerListState fix your ListView like this:
#override
Widget build(BuildContext context) {
return ListView.builder(
physics: ClampingScrollPhysics(),
shrinkWrap: true,
itemCount: widget.workers.length,
itemBuilder: (context, index) {
return Column(
children: <Widget>[
Card(
child: new ListTile(
selected: widget.workers[index].isSelected,
leading: const Icon(Icons.info),
title: new Text(widget.workers[index].Fullname),
subtitle: new Text(widget.workers[index].UserId),
onTap: (){
//this will select the deselected item
//and will deselect the selected item
setState(() {
widget.workers[index].isSelected != widget.workers[index].isSelected
});
}
),),
],
);
},
);
}