I am writing a flutter app and am trying to insert a DataTable. I am having issues with the text getting truncated though and not overflowing to a new line. It will do 2 lines but not 3 on the device I am on. I tried adding the text in an Expanded widget, but that won't work and throws a 'Incorrect use of ParentDataWidget'. Any way to get really long text to span 3, 4, or 5+ lines? Below is the code.
import 'package:flutter/material.dart';
class ClaimTypeTable extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
child: new DataTableWidgetExtract(),
);
}
}
class DataTableWidgetExtract extends StatelessWidget {
const DataTableWidgetExtract({
Key key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return DataTable(
columnSpacing: 10.0,
horizontalMargin: 5,
columns: <DataColumn>[
DataColumn(label: Text('Type')),
DataColumn(label: Text('Description',)),
],
rows: claimTypesList
.map(
(itemRow) => DataRow(
cells: [
DataCell(Text(itemRow.shortName),
showEditIcon: false, placeholder: false, onTap: () {
print('We tapped it - ${itemRow.shortName}');
}),
DataCell(
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(itemRow.description,),
),
showEditIcon: false,
placeholder: false,
),
],
),
)
.toList(),
);
}
}
class ClaimType {
String shortName;
String description;
ClaimType({
this.shortName,
this.description,
});
}
var claimTypesList = <ClaimType>[
ClaimType(
shortName: 'Fire',
description:
'There was a fire. The fire department may or may not have been called, but shit got burnt up.',
),
ClaimType(
shortName: 'Water',
description:
'Water damage. Examples of typical water damage include: burst pipe, broken water heater, or faulty toilet.',
),
ClaimType(
shortName: 'Wind',
description:
'There is non-hurricane wind damage to your residence. If it is related to a hurricane, please select Hurricane as the claim type instead. This one is really long and gets truncated.',
),
ClaimType(
shortName: 'Crime',
description:
'Vandalism, theft, or other criminal behavior that resulted in a loss.'),
];
And here is the image of what it looks like:
Three to four things to apply-
Inside the DataCell widget you need to use a ConstrainedBox. In the ConstrainedBox specify the maxWidth and minWidth to cover the height.
In Datacell widget's child if Text widget give overflow: TextOverflow.visible, softWrap: true,
In DataTable widget give dataRowHeight property.
Data tables are so hard to work with. I just ended up using a listview with dividers.
Related
I am a beginner in Flutter programming, and I am in the learning phase. I am trying to create only the UI of a list using dummy data for the item which can be bought very frequently by the customer. for example a customer has bought pencils very often, and a pen not so often, so the pencils will be on the top of the list and pen will be below the pencils and so on...! Below is the image which I wanted to create
waiting for your suggestions. thanks
in short frequently bought items are on the top of the list.
List dataItems = [
{"product": "pencil", "frequency" :4},
{"product": "pencil2", "frequency" :4},
{"product": "pencil4", "frequency" :4}
];
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Row(children: const[
Text("No"),
SizedBox(width: 16),
Text("Product"),
SizedBox(width: 16),
Text("Frequency"),
]),
Expanded(
child: ListView.builder(
itemCount: 2,
itemBuilder: (ctx, index){
return Row(children: [
Text(index.toString()),
SizedBox(width: 16),
Text(dataItems[index]["product"]),
SizedBox(width: 16),
Text(dataItems[index]["frequency"].toString()),
Spacer(),
MaterialButton(onPressed: (){}, child: Text("Deliver"), ),
MaterialButton(onPressed: (){}, child: Text("Self Pickup"), )
]);
}
))
],
),
You can use an implement like this, try using row methods in order to divide items inside the row.
i hope it little help
class DemoWork extends StatefulWidget {
const DemoWork({Key? key}) : super(key: key);
#override
State<DemoWork> createState() => _DemoWorkState();
}
class _DemoWorkState extends State<DemoWork> {
List product=[
{'product':'pencil', 'frequency': 10}, {'product':'pen','frequency':24}, {'product':'notebook','frequency':12}, {'product':'markers','frequency':2}, {'product':'erasers','frequency':21}
];
Iterable<dynamic>? data;
#override
void initState() {
// TODO: implement initState
super.initState();
product.sort((a, b) {
return a['frequency'].compareTo(b['frequency']);
});
//output is {product: pen, frequency: 24}, {product: erasers, frequency: 21}, {product: notebook, frequency: 12}, {product: pencil, frequency: 10}, {product: markers, frequency: 2}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView.builder(
itemCount: 5,
shrinkWrap: true,
reverse: true,// because list show low to high so reverse use to show high to low value and shrink wrap adjust its size use as you want to show and adjust it
itemBuilder: (context, index) {
return ListTile(
leading:Text(index.toString()) ,
title: Text(product[index]['product']),
trailing: Text(product[index]['frequency'].toString()),
);
}),
);
}
}
You can counting time user buying stuffs and use it to sort the list. Of course u need to build your widget for yourself, i will only suggest the logic.
Example
void main() {
var stuffs = ['ball', 'pen', 'pencil', 'glass']; // your stuff here
var history = ['pencil', 'pencil', 'glass', 'pen', 'glass', 'pencil']; // your buy history here, you can add more if you want
print('stuff will dispaly in order: $stuffs');
stuffs.sort((a,b) => history.timesOf(b).compareTo(history.timesOf(a))); // Function that sort the list by 'buy times' of user store in `history` variable
print('stuff will dispaly in order when using history: $stuffs');
}
extension HistoryCheck on List<String> {
int timesOf(String name) => where((_) => _ == name).length;
}
// ===== Result =====
stuff will dispaly in order: [ball, pen, pencil, glass]
stuff will dispaly in order when using history: [pencil, glass, pen, ball]
Suppose you have the following product model and list of products:
// Product Model
class Product {
final int serialNo;
final String name;
final int frequency;
const Product(this.serialNo, this.name, this.frequency);
}
// List of products
List<Product> data = [
Product(1, 'Pencil', 35),
Product(2, 'Pen', 30),
Product(3, 'Notebook', 25),
];
Just before showing this list of products in a list view, you can sort it based on the frequency as shown below:
data.sort((a, b) => b.frequency.compareTo(a.frequency));
I'm new to flutter. I'm creating a app with a table which a button in every row. I need to delete the row when that button is pressed.
This is the code of the table.
DataTable(
columns: const <DataColumn>[
DataColumn(label: Text('Medications')),
DataColumn(label: Text('Amount')),
DataColumn(label: Text('When')),
DataColumn(label: Text(' '))
],
rows:
_data // Loops through dataColumnText, each iteration assigning the value to element
.map(
((element) => DataRow(
cells: <DataCell>[
DataCell(Text(element[
"drug"])), //Extracting from Map element the value
DataCell(Text(element["amount"])),
DataCell(Text(element["when"])),
DataCell(new RButton(
id: bid,// bid variable increments by 1 every t
onPressed: onPressed,
))
],
)),
)
.toList(),
),
This is the code of RButton
class RButton extends StatelessWidget {
final int id;
final Function onPressed;
const RButton({this.id, this.onPressed});
#override
Widget build(BuildContext context) {
return SizedBox(
width: 30,
child: FlatButton(
onPressed: () {
onPressed(this.id);
print(this.id);
},
textColor: Colors.red,
child: Text("-"),
));
}
}
This is the code of the function button run when pressed.
onPressed(id) {
setState() {
_data.remove(_data[id]);
}
}
Assuming that you are properly using a StatefulWidget to create this example.
setState is a function that takes another function as it's single parameter.
So, it must used like this,
onPressed(id) {
setState(() {
_data.remove(_data[id]);
});
}
I want a very SIMPLE way of populating my DataTable dynamically and being able to refresh it when new data is added so it updates & rebuilds instantly. Using Hive boxes and in the simplest fashion possible.
I am using Hive encrypted boxes, but that does not even matter
I am posing this question with an answer below. I spent a ton of time to discover this, as I couldn't find anything else similar using Hive boxes & SIMPLE. I truly hope this helps others, I've gotten tons of help on SO as a green dev. I am quite proud that I could possibly return the favours.
I have a class with adapter hooked up & registered to Hive
import 'package:hive/hive.dart';
part 'person.g.dart';
#HiveType(typeId: 0)
class Person {
#HiveField(0)
final String firstName;
#HiveField(1)
final String lastName;
#HiveField(2)
final int age;
#HiveField(3)
final String Status;
Person(
this.firstName,
this.lastName,
this.age,
this.status,
);
#override
String toString() {
return '{${this.firstName}, ${this.lastName}, ${this.age}, ${this.status}}';
}
}
Saving to the hive on button press
onPressed: () {
final newPersonData = Person(
_firstName,
_lastName,
int.parse(_age),
_status,
);
addPerson(newPersonData);
var box = Hive.box(personTable);
for (var index in box.values) {
print(index);
}
},
DataTable build method
_buildDataTable() {
return SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: DataTable(
columns: const <DataColumn>[
DataColumn(
label: Text('First'),
),
DataColumn(
label: Text('Last'),
),
DataColumn(
label: Text('Age'),
),
DataColumn(
label: Text('Status'),
),
],
rows: List<DataRow>
// How to dynamically load cells in a ***SIMPLE*** manner?
);
},
),
),
);
}
DataTable build method
_buildDataTable() {
final hiveBox = Hive.box(personTable);
return ValueListenableBuilder(
valueListenable: Hive.box(personTable).listenable(),
builder: (context, personBox, _) {
return SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: DataTable(
columns: const <DataColumn>[
DataColumn(
label: Text('First'),
),
DataColumn(
label: Text('Last'),
),
DataColumn(
label: Text('Age'),
),
DataColumn(
label: Text('Status'),
),
],
rows: List<DataRow>.generate(
hiveBox.length,
(index) {
final person = hiveBox.getAt(index) as Person;
return DataRow(
cells: [
DataCell(Text(person.firstName)),
DataCell(Text(person.lastName)),
DataCell(Text(person.age.toString())),
DataCell(Text(person.status))
],
);
},
),
),
);
},
);
}
Explanation
I'll explain as much as I know about what I'm doing above.
ValueListenableBuilder- this method listens for additions to the Hive.box and refreshes if any new updates occur. Without this method the DataTable will not rebuild when data is added.
The DataRow.generate() method allows you to iterate through whatever you want to iterate through, in my case its Strings which come from my Person object which in turn come from my encrypted Hive.
Thats pretty much it! Super simple- hope this can help others :)
EDIT: it was brought to my attention that watch() should be used instead of ValueListenableBuilder() as well as an important fact that this application renders the entire chart. For my application this is okay because I'm not building with much data. Food for thought.
I am trying to create a checkbox list like this:
my plan
I used CheckBoxListTile with dense and contentPadding to remove all possible paddings, and this is what I have:
my result
Is there any way to remove the extra "padding" between CheckBoxListTile?
Here is my code (a single checkbox):
class Checklist extends StatefulWidget {
Checklist({#required this.content});
final String content;
#override
_ChecklistState createState() => _ChecklistState();
}
class _ChecklistState extends State<Checklist> {
bool _checked = false;
#override
Widget build(BuildContext context) {
return CheckboxListTile(
contentPadding: EdgeInsets.all(0),
//dense: true,
title: Text(widget.content, style: TextStyle(color: kBlackColor),),
value: _checked,
onChanged: (bool value) {
setState(() {
_checked = value;
});
},
controlAffinity: ListTileControlAffinity.leading,
activeColor: kBlackColor,
);
}
}
Multiple checkboxes:
Column(
children: [
Checklist(content: 'Develop an app'),
Checklist(content: 'Develop a good app'),
Checklist(content: 'Develop a really good app'),
],
),
wrap your CheckBox inside SizedBox will resize the padding of the check box
SizedBox(
height: whatYouWant,
width: whatYouWant,
child: Checkbox(...),
)
please check this solution: https://stackoverflow.com/a/59420505/11989529
When a PopupMenuButton is pressed, the currently selected value is highlighted,
but when a DropdownButton is pressed, the currently selected value is not highlighted.
Is there a way to highlight the selected value of a DropdownButton?
For reference here is some sample code:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(home: MyHomePage());
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
String letter = 'A';
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Popup Menu Button')),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
SizedBox(height: 16.0),
Text('PopupMenuButton'),
buildPopupMenuButton(),
SizedBox(height: 16.0),
Text('DropdownButton'),
buildDropdownButton(),
],
),
);
}
Widget buildPopupMenuButton() {
return PopupMenuButton<String>(
padding: EdgeInsets.zero,
initialValue: letter,
onSelected: (val) => setState(() => letter = val),
child: ListTile(
title: Text('The letter $letter'),
),
itemBuilder: (BuildContext context) {
return <PopupMenuItem<String>>[
PopupMenuItem<String>(
value: 'A',
child: Text('The letter A'),
),
PopupMenuItem<String>(
value: 'B',
child: Text('The letter B'),
),
];
},
);
}
Widget buildDropdownButton() {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: DropdownButton<String>(
value: letter,
onChanged: (val) => setState(() => letter = val),
items: [
DropdownMenuItem<String>(
value: 'A',
child: Text('The letter A'),
),
DropdownMenuItem<String>(
value: 'B',
child: Text('The letter B'),
),
],
),
);
}
}
Here's a video that shows the issue:
The DropdownMenuItem doesn't support many custom modifications on the child element, as there's no style, background, anything actually in the DropdownMenuItem attributes to help you with that. Looking at the code, it really wasn't built for that,
Yet, there's something you could add, a simple check on the child attribute of the DropdownMenuItem, and wrap the Text child element in something else or style the Text element itself if it is checked.
One example:
Widget buildDropdownButton() {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: DropdownButton<String>(
value: letter,
onChanged: (val) => setState(() => letter = val),
items: [
DropdownMenuItem<String>(
value: 'A',
child: Container(
color: letter == 'A' ? Colors.black12 : null,
child: Text('The letter A'),
),
),
DropdownMenuItem<String>(
value: 'B',
child: Container(
color: letter == 'B' ? Colors.black12 : null,
child: Text('The letter B'),
),
),
],
),
);
}
Note that in a real case scenario, you would have a method with a paremeter to build each dropdown item, so the verification wouldn't have to be hardcoded like letter == 'A'.
This would be the output:
This approach allows you to style a bit, but it has an ugly result in some cases. Although it is customizable, there will always be a white margin around the item, and it also shows the same styles when the dropdown list is closed, so it gets a bit ugly on the main page.
Instead of changing the background, you can also change text colors, underline, icons on the side, something like that make it much better, like:
DropdownMenuItem<String>(
value: 'A',
child: Text('The letter A',
style: TextStyle(
color: letter == 'A' ? Colors.red : Colors.black87,
),
),
)
Well, as far as I know this grey overlay is a so called 'Ripple effect' in the material design library. It seems that Flutter does not adapt the full design in all widgets yet.
However you can try to use the InkWell widget to add this kind of animations/colors to current widgets:
https://flutter.io/docs/cookbook/gestures/ripples
E.g:
PopupMenuItem<String>(
value: 'B',
child: InkWell(child: Text('The letter B'))
),
I am not sure if the width will be correct, but at least it should show the grey overlay when you press on the entry.
You can also check the Flutter source:
https://github.com/flutter/flutter/blob/237fc2fb45639312001e947bf7465ef9f23bb699/packages/flutter/lib/src/material/popup_menu.dart#L933
Here you can see that a Inkwell is standard being used for the PopupMenuButton.
Responding to your original issue which was: "I'm interested in the darker background from the currently selected value when all of the values are shown."
Your PopupMenuButton will look at its initialValue: parameter each time it is opened--the item corresponding to this value will be highlighted. You will need to update the initialValue parameter each time using the onSelected function.
Make sure the parent widget is a StatefulWidget widget and create a reference to whatever your initialValue is. The PopupMenuButton has an onSelected parameter that takes in a function with parameter String.
Whenever you select an option from the PopupMenuButton, call
setState(() {
...
this.initialValue = value;
});
The full class will look something like this.
Class YourClass extends StatefulWidget {
#override
createState() => _YourClassState();
}
class _YourClassState extends State<YourClass> {
...
String initialValue = 'foo';
#override
Widget build(BuildContext context) {
final items = [
PopupMenuItem(
value: 'foo',
child: Text('foo'),
),
PopupMenuItem(
value: 'nice',
child: Text('nice'),
),
}
return Scaffold(
appBar: ...,
drawer: ...,
body: PopupMenuButton(
icon: ...,
itemBuilder: (_) => items,
initialValue: this.initialValue,
onSelected: (value) => bar(value),
),
);
}
void bar(String value) {
setState(() {
...
this.initialValue = value;
});
}
}
You can wrap the widget with Theme to set a highlight color.
return Theme(
data: ThemeData(highlightColor: Colors.grey[300]),
child: DropdownButton()
You may try it:
class CustomDropdownMenuItem<T> extends DropdownMenuItem<T> {
const CustomDropdownMenuItem({
super.key,
super.onTap,
super.value,
super.enabled = true,
super.alignment,
required this.current,
required super.child,
});
final T current;
#override
Widget build(BuildContext context) {
return Container(
color: current == value ? Theme.of(context).highlightColor : null,
child: super.build(context),
);
}
}
However, the element will not be completely covered in color. You can also add a check on the current device to exclude those that work correctly (web and desktop).
Basically, we have to wait for this issue to be solved.
Update:
Alternatively, you can use color selection if you use Text:
final theme = Theme.of(context);
...
return DropdownMenuItem<AppLocale>(
value: value,
onTap: () => {},
child: Text(
value.name,
style: theme.textTheme.titleMedium?.copyWith(
color: value == current ? theme.colorScheme.secondary : null),
),
);