Flutter - Creating tables dynamically - dynamic

I'm trying to dynamically create tables using Dart and Flutter. Something like this The number of table rows will change depending on the the JSON file passed in.
I've read through and done all of the Flutter tutorials I can get my hands on and read through the Documentation on the Table and ListBuilder classes, but none of them quite accomplish what I'm trying to do, because the examples either only dynamically create single ListItems or all the data and/or Widgets are hard-coded.
I've also tried doing this by doing:
Table dynamicTable = new Table(); then dynamically adding children Widgets with
dynamicTable.add(TableRow(
children: [
Text("test1"),
Text("test2"),
Text("test3"),
]
));
But I get an error saying "Cannot add to an unmodifiable list".
Any tips on how to accomplish this would be greatly appreciated.

This function creates a table dynamically:
Widget createTable() {
List<TableRow> rows = [];
for (int i = 0; i < 100; ++i) {
rows.add(TableRow(children: [
Text("number " + i.toString()),
Text("squared " + (i * i).toString()),
]));
}
return Table(children: rows);
}

First fetch List of Records
List<PriceDetailsView> priceDetailsList = MockDataSource.getPriceDetailsDataList();
Now create an empty list of table rows :
List<TableRow> priceTableRows = [];
Add details from the fetched list to this row list:
for(PriceDetailsView priceDetalis in priceDetailsList){
priceTableRows.add(TableRow(children: [Text(priceDetalis.priceType), Text(priceDetalis.priceValue)]));
}
Now create your table with this list of row :
Table priceTable = Table(children: priceTableRows);

Suppose you have this model class:
class Person {
final int id;
final String name;
final String email;
final String phone;
const Person({
this.id = 0,
this.name = '',
this.email = '',
this.phone = '',
});
...
}
and you have populated a List of Person with data from an API call:
List<Person> _personList = ...
To generate the TableRow, you can do it using List.generate Dart function:
return Table(
border: TableBorder.all(color: Colors.black),
children: List<TableRow>.generate(
_personList.length,
(index) {
final person = _personList[index];
return TableRow(
children: [
Padding(
padding: EdgeInsets.all(5.0),
child: Text(person.id.toString(), textAlign: TextAlign.center),
),
Padding(
padding: EdgeInsets.all(5.0),
child: Text(person.name, textAlign: TextAlign.center),
),
Padding(
padding: EdgeInsets.all(5.0),
child: Text(person.email, textAlign: TextAlign.center),
),
Padding(
padding: EdgeInsets.all(5.0),
child: Text(person.phone, textAlign: TextAlign.center),
),
],
);
},
growable: false,
),
);

It's pretty easy, actually! All you have to do is, make a list of TableRows, and put that in the children parameter of your table. For example
List<TableRow> tableRows = [];
// dynamically make TableRows and add them to the list
And then you can just do this:
Table(
children: tableRows,
// other stuff
)

Related

How to fetch saved data from SQLite database in a screen for a specific transaction?

I am making an app in which users can save the transaction data which they can do by filling out the following form
The transactions then will be saved in the database and can be shown to users on the following screen
The details which are getting saved by the form are Name, Amount Comment, Category, date and if the transaction is unnecessary. But the details which I want to show in image 2 are Name, amount and date. Now for viewing the rest of the details the user can click on the specific transaction and a new page should open showing the details of the transaction like this
How can I do it? I mean if I click on the transaction above page should display.
I am creating my database using the following code:
await database.execute(
"CREATE TABLE $TABLE_EXPENSES ("
"$COLUMN_ID INTEGER PRIMARY KEY,"
"$COLUMN_NAME TEXT,"
"$COLUMN_AMOUNT INTEGER,"
"$COLUMN_UNNECESSARYEXPENSES INTEGER,"
"$COLUMN_CATEGORY TEXT,"
"$COLUMN_DATETIME TEXT,"
"$COLUMN_COMMENTS TEXT"
")",
);
Edit:
The code which I am using for mapping:
class expense {
int id;
String name;
int amount;
bool isUnnecessaryExpenses;
String category;
DateTime dateTime;
String pickedDate;
String comments;
expense({this.id, this.name, this.amount, this.isUnnecessaryExpenses, this.category, this.dateTime, this.pickedDate, this.comments});
Map toMap() {
var map = {
DatabaseProvider.COLUMN_NAME: name,
DatabaseProvider.COLUMN_AMOUNT: amount,
DatabaseProvider.COLUMN_UNNECESSARYEXPENSES: isUnnecessaryExpenses ? 1 : 0,
DatabaseProvider.COLUMN_CATEGORY: category,
DatabaseProvider.COLUMN_DATETIME: pickedDate,
DatabaseProvider.COLUMN_COMMENTS: comments,
};
if (id != null) {
map[DatabaseProvider.COLUMN_ID] = id;
}
return map;
}
expense.fromMap(Map map) {
id = map[DatabaseProvider.COLUMN_ID];
name = map[DatabaseProvider.COLUMN_NAME];
amount = map[DatabaseProvider.COLUMN_AMOUNT];
isUnnecessaryExpenses = map[DatabaseProvider.COLUMN_UNNECESSARYEXPENSES] == 1;
category= map[DatabaseProvider.COLUMN_CATEGORY];
pickedDate = map[DatabaseProvider.COLUMN_DATETIME];
comments = map[DatabaseProvider.COLUMN_COMMENTS];
}
}
And I am displaying transactions in image 2 with the help of following code:
ListTile(
leading: CircleAvatar(
backgroundColor: Colors.indigo,
foregroundColor: Colors.white,
child: Text(
expensess.name
.substring(0, 1)
.toUpperCase(),
style: TextStyle(fontSize: 32, fontWeight: FontWeight.bold),
),
),
title: Text(
expensess.name ?? "Title",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 30,
color: Colors.black,
),
),
subtitle: Text(
"Amount: ${expensess.amount.toString()}"
"\nDate: ${expensess.pickedDate}\n"
,style: TextStyle(
fontStyle: FontStyle.italic,
fontSize: 20,
color: Colors.black,
),
),
onTap: () {
Navigator.push(context, MaterialPageRoute(builder: (context)=> ViewTransaction()));
}
));
}
viewTransaction() is as follows:
class ViewTransaction extends StatefulWidget {
#override
_ViewTransactionState createState() => _ViewTransactionState();
}
class _ViewTransactionState extends State {
#override
void initState(){
super.initState();
DatabaseProvider.db.getExpenses().then(
(expenseList) {
BlocProvider.of<ExpenseBloc>(context).add(SetExpenses(expenseList));
},
);
}
Widget build(BuildContext context) {
expense expenses;
return Scaffold(
appBar: AppBar(backgroundColor: Colors.white, title: Text(expenses.name),),
);
}
}
Please help.
Thanks for your replies.

How to put json-data into a list in flutter?

When I try to get data from this API https://api.met.no/weatherapi/locationforecast/2.0/complete?lat=10&lon=10 it gets me a long array of some sort with all the timeseries. In the end, I would like to display some data from each time which has its own place in the downloaded array. I want to covert all data to a list so I can manipulate the data but i get errors like these type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'String.
This is my code
List<dynamic> timeseriesglobal = [];
void loadForecast() async{
//Getting the data from API
Response response = await get("https://api.met.no/weatherapi/locationforecast/2.0/complete?lat=57.047218&lon=9.920100");
var results = jsonDecode(response.body);
timeseriesglobal = results["properties"]["timeseries"] as List;
}
And in the end i have this code for displaying the data
child: ListView.builder(
itemCount: timeseriesglobal.length,
itemBuilder: (context,index){
return Card(
child: ListTile(
title: Text(
timeseriesglobal[index]
),
),
);
},
What am I doing wrong? Please help me
Provide the property name that you want to show Ex time
ListView.builder(
itemCount: timeseriesglobal.length,
itemBuilder: (context,index){
return Card(
child: ListTile(
title: Text(
timeseriesglobal[index]['time']
),
),
);
},
Create Your BaseModel from the json you are getting from the link.
Then Parse like below
var data= BaseModel.fromJson(response.body);
Now this will contain everything and you can extract whatever u want from the model
To convert the json use this link

how to add a text at the bottom of jspdf auto table in angular5

I am using jspdf auto table in my angular5 app. I am generating dynamic data for jspdf auto table. My problem is how can I add a text at the bottom of table?
const columnHead = [];
const columnRow = [];
this.tableColumns.filter(x => {
columnHead.push(this.capFirstLtr(x.title));
});
this.tableData.filter((x, y) => {
x.slaMetFlag = (this.mapData[y].slaMetFlag ? this.capFirstLtr(this.mapData[y].slaMetFlag) : '-');
columnRow.push([x.date, x.slaMetFlag, x.srType, x.srID, x.srStatus, x.aof, x.ppm, x.deviceID, x.siteID, x.branchID]);
});
const doc = new jsPDF();
doc.text('Showing data from ' + moment(this.requestApi.fromDate).format('MM/DD/YYYY') + ' to ' + moment(this.requestApi.toDate).format('MM/DD/YYYY'), 5, 10);
doc.autoTable(columnHead, columnRow, {
styles: { overflow: 'linebreak', halign: 'center', fontSize: 7},
tableWidth: 200,
margin: 5,
theme: 'grid',
startY: 15,
addPageContent: function(data) {
console.log('data', data);
}
});
doc.save('SR View Export');
}
in the above code how can I add a text after the completion of jspdf auto table?
If you want to know something about the last table that was drawn you can use doc.autoTable.previous. It has a doc.autoTable.previous.finalY property among other things that has the value of the last printed y coordinate on a page. This can be used to draw text, multiple tables or other content after a table.
doc.autoTable.previous.finalY use this property.
doc.autoTable(headers, data);
let finalY = doc.autoTable.previous.finalY;// The y position on the page
doc.text(20, finalY, "Hello!")

Strange red Block under Flutter TextField Widget

I'm working on a shopping list in one tab of my flutter app but under the Input field i always get and strange red block when the keyboard comes up (red block stays there until keyboard goes away)
RedBlock which appears with keyboard
Debug report which shows up after clicking into the field
Performing full restart...
Restarted app in 1.172ms.
D/ViewRootImpl#da3a8cd[MainActivity](28869): ViewPostImeInputStageprocessPointer 0
D/ViewRootImpl#da3a8cd[MainActivity](28869): ViewPostImeInputStageprocessPointer 1
I/flutter (28869): [{name: Lukas, id: 1, value: 32}, {name: Sophie, id: 2, value: 20}, {name: Peter, id: 3, value: 45}]
D/ViewRootImpl#da3a8cd[MainActivity](28869): ViewPostImeInputStage processPointer 0
D/ViewRootImpl#da3a8cd[MainActivity](28869): ViewPostImeInputStage processPointer 1
V/InputMethodManager(28869): Starting input: tba=android.view.inputmethod.EditorInfo#b63ece2 nm : com.yourcompany.flutterapp ic=io.flutter.plugin.editing.InputConnectionAdaptor#484e873
I/InputMethodManager(28869): [IMM] startInputInner - mService.startInputOrWindowGainedFocus
D/InputTransport(28869): Input channel constructed: fd=101
D/InputTransport(28869): Input channel destroyed: fd=100
D/InputMethodManager(28869): ISS - flag : 0Pid : 28869 view : com.yourcompany.flutterapp
D/ViewRootImpl#da3a8cd[MainActivity](28869): MSG_RESIZED: frame=Rect(0, 0 - 1080, 2220) ci=Rect(0, 63 - 0, 918) vi=Rect(0, 63 - 0, 918) or=1
D/ViewRootImpl#da3a8cd[MainActivity](28869): Relayout returned: oldFrame=[0,0][1080,2220] newFrame=[0,0][1080,2220] result=0x1 surface={isValid=true -887126016} surfaceGenerationChanged=false
Here you can see my code i have written:
import 'dart:async';
import 'dart:core';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:path_provider/path_provider.dart';
import 'package:sqflite/sqflite.dart';
class ShoppingBasket extends StatefulWidget {
#override
ShoppingBasketState createState() => new ShoppingBasketState();
}
class ShoppingBasketState extends State<ShoppingBasket> {
Directory documentsDirectory;
String dirPath;
Database database;
List<Map> listRecords;
Widget listView;
final TextEditingController _controller1 = new TextEditingController(); // name field
final TextEditingController _controller2 = new TextEditingController(); // value field
#override
void initState() {
listView = beforeDataFetchIsFinished();
getPathAndCheckForDbAndPrepareListView();
}
#override
Widget build(BuildContext context) {
return new Scaffold(
body: new Column(
children: <Widget>[
inputFieldCard(),
listView, //--> List view gets after all data was fetched here
],
),
);
}
//-----------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------
//View Build ------------------------------------------------------------------------
/// Set the listview variable with an CircularPorgressIndicator.
/// gets overriden if the real listview has finished.
Widget beforeDataFetchIsFinished() {
return new Container(
margin: new EdgeInsets.fromLTRB(0.0, 30.0, 0.0, 0.0),
child: new Center(
child: new CircularProgressIndicator(
strokeWidth: 2.0,
),
),
);
}
/// The Inputfield card in one methode.
/// Returns the InputCard as one widget.
Widget inputFieldCard() {
return new Container(
child: new Card(
child: new Container(
child: new Column(
children: <Widget>[
new Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Container(
width: 150.0,
padding: new EdgeInsets.fromLTRB(10.0, 20.0, 10.0, 10.0),
child: new TextField(
controller: _controller1,
decoration: new InputDecoration(
hintText: 'Name...',
),
),
),
new Container(
width: 150.0,
padding: new EdgeInsets.fromLTRB(10.0, 20.0, 10.0, 10.0),
child: new TextField(
keyboardType: TextInputType.number,
controller: _controller2,
decoration: new InputDecoration(
hintText: 'Value...',
),
),
),
],
),
new Container(
padding: new EdgeInsets.fromLTRB(0.0, 0.0, 0.0, 20.0),
child: new RaisedButton(
color: Colors.green,
child: new Text('Insert Data', style: new TextStyle(color: Colors.white),),
onPressed: () {
insertToDb(_controller1.text, _controller2.text);
_controller1.clear();
_controller2.clear();
},
),
),
],
)
),
), //top card end
);
}
/// the CircularProgressIndicator gets overiden if this
/// methode gets all its data --> then rerender.
Widget injectListViewAfterAllDataIsFetched() {
return new Card(
child: new Container(
child: new ListView.builder(
shrinkWrap: true, //<-- Necessary because Listveiw inside Column
itemCount: listRecords == null ? 0 : listRecords.length,
itemBuilder: (BuildContext context, int index) {
return new ListTile(
title: new Text(listRecords[index]['name']),
);
},
),
),
);
}
//-----------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------
//Data-Base Operations --------------------------------------------------------------
/// Start up --> Open db and fetching data when complete
/// start render engine again.
Future<bool> getPathAndCheckForDbAndPrepareListView() async {
documentsDirectory = await getApplicationDocumentsDirectory();
String dirPath = documentsDirectory.path;
List content = documentsDirectory.listSync();
final File file = new File(dirPath + '/myDataBase.db');
if(!content.contains(file)) { //Check if db exists
await createDbIfNotExists(file); //if not create it
}
print(await getRecords());
listRecords = await getRecords();
print(listRecords);
setState(() {
listView = injectListViewAfterAllDataIsFetched();
});
return true;
}
/// Inserting data into the data base.
/// #return true.
Future<bool> insertToDb(String name, String value) async {
if(name != '' && value != '') {
var valueSql = int.parse(value);
String sql = 'INSERT INTO Test(name, value) VALUES("$name", $valueSql)';
await database.inTransaction(() async {
await database.rawInsert(sql);
});
listRecords = await getRecords();
setState(() {
listView = injectListViewAfterAllDataIsFetched();
});
return true;
} else {
return false;
}
}
/// Gives the whole Db back.
/// #return Map with all records.
Future<List<Map>> getRecords() async {
return await database.rawQuery('SELECT * FROM Test');
}
/// Creating the given File (should be an .db file).
/// #param file Gives the file (.db) which gets created.
/// #return true.
Future<bool> createDbIfNotExists(File file) async {
database = await openDatabase(file.path, version: 1,
onCreate: (Database db, int version) async {
await db.execute(
"CREATE TABLE Test (id INTEGER PRIMARY KEY, name TEXT, value INTEGER)");
});
return true;
}
}
Does someone of you understand why this is showing up? And have an smart Solution to fix it?
Edit: Some Photos which show my Keyboard with shortcuts and without them
I was facing the same problem. Just set resizeToAvoidBottomPadding false in you scaffold and it should solve the problem.
The red bar indicates that the content of one of your containers or columns are bigger, than allowed by their parent. I cant reproduce this issue on iOS or Android. Also, why is the raised button not displayed in your screenshot?

Add row number column to table

I'm trying to do something simple: display the row number on a react-bootstrap-table column. Problem is that the only way it seems possible is by adding a data field with indexes, which get scrambled if sorting is turned on.
Ideally there would be a method that allows for this but I can't seem to find any.
I had the same issue.
add
TableHeaderColumn
like this:
<BootstrapTable data={data}>
...
<TableHeaderColumn dataField="any" dataFormat={indexN}>#</TableHeaderColumn>
...
</BootstrapTable>
and function:
function indexN(cell, row, enumObject, index) {
return (<div>{index+1}</div>)
}
For now there is a big change in the react-bootstrap-table2, that there's no TableHeaderColumn, instead you are supposed to be define the "columns" prop on BootstrapTable. I solve this issue by using columns formatter and data mapping like so:
In the Backend:
const ret = data.map((d, i) => {
return { num: i + 1, id: d.id, name: d.name, age: d.age}
});
return ret;
In the Frontend:
const columns = [{
dataField: 'id',
text: '#',
sort: true,
headerStyle: () => {
return { width: '10%', textAlign: 'center' }
},
formatter: (rowContent: any, row: any) => {
return (
<div>
{row.num}
</div>
)
}
}
Ref: https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/migration.html