How to align the text within ListTile? - flutter

Is there any other way for me to align the text within the leading and title of a ListTile without using the Row widget? The data was fetched from Firebase and then being viewed using Listview. Builder.
FutureBuilder(
future: fetchData(),
builder: (context, snapshot) {
if (snapshot.hasData) {
List<taskData> result = snapshot.data as List<taskData>;
return ListView.builder(
scrollDirection: Axis.vertical,
itemCount: result.length,
itemBuilder: (BuildContext context, int i) {
DateTime updateDate = DateTime.fromMillisecondsSinceEpoch(result[i].date);
String formDate = DateFormat('yMMMd').format(updateDate);
return GestureDetector(
onTap: () {
NavigationController(context, 'Edit', 'Title',
result[i].task, result[i].id, updateDate);
},
child: Card(
child: ListTile(
leading: Text(formDate, style: TextStyle(fontFamily: 'ProximaNova', fontSize: 16),),
title: Text('${result[i].task}',style: TextStyle(fontFamily: 'ProximaNova',fontSize: 18),),
trailing: IconButton(
icon: Icon(Icons.delete),
onPressed: () {
id = result[i].id;
showDialog<void>(
context: context, builder: (context) => dialog);
},
),
),
),
);
});
} else if (snapshot.hasError) {
print('${snapshot.error}');
return Text(
"",
style: TextStyle(fontSize: 20),
);
}
// By default, show a loading spinner.
return Center(
child: Container(
margin: EdgeInsets.symmetric(vertical: 100.0),
child: CircularProgressIndicator(),
));
},
),
Here is the picture for reference ('title' is not aligned with 'leading' and 'trailing'):

It is working for you
ListTile(
leading: Text('2021-04-27'),
title: Text('Lion', textAlign: TextAlign.center,),
trailing: IconButton(
icon: Icon(Icons.delete),
onPressed: () {},
),
),

Sorry if I'm not solving the problem asked but as a suggestion how about wrapping it in a Row Widget instead of a List Tile where you center align it?

first of all i believe you leading is not aligned with title and trailing, i do not know due to what reason this is happening from your code. solution might be Put your both text in title with + operator. or in leading whichever helps your problem get solved.

Related

How to show insert data into list view builder without reload application in flutter

I have created a sqlite crud application from flutter. my app data insert into the table successfully. but when i show that inserted date from listview need to reload app.in my app data insert from a separate screen. data have shown on home screen. i want show data without reload app in listview how can I slove my problem?
Here is my app code. this is my home screen. this screen show data from listview.
FutureBuilder<List<Student>>(
future: DatabaseHelper.instance.getStudentDetails(),
builder: (BuildContext context,
AsyncSnapshot<List<Student>> snapshot) {
if (!snapshot.hasData) {
return Center(child: Text('Loading...'));
}
return snapshot.data!.isEmpty
? Center(child: Text('No Student Details in List.'))
: ListView(
children: snapshot.data!.map((student) {
return Center(
child: Card(
color: selectedId == student.id
? Colors.white70
: Colors.white,
child: ListTile(
title: Text(
'Student Name : ${student.name}'),
subtitle:
Text('Course : ${student.course}'),
onTap: () {},
onLongPress: () {
setState(() {
DatabaseHelper.instance
.remove(student.id!);
});
},
),
),
);
}).toList(),
);
}),
This is my second screen.this screen I adding data insert into sqlite table.when I click floating action button I want to show insert data in home screen list view without reload.
Column(
children: [
SizedBox(
height: 10.0,
),
Text(
'Enter Student Details',
style: TextStyle(fontSize: 20.0, fontWeight: FontWeight.bold),
),
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Divider(color: Colors.black),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
decoration: InputDecoration(
label: Text('Enter Name :'),
),
controller: nameController,
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
decoration: InputDecoration(
label: Text('Enter Course :'),
),
controller: courseController,
),
),
],
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.save),
onPressed: () async {
await DatabaseHelper.instance.add(
Student(name: nameController.text, course: courseController.text),
);
setState(() {
nameController.clear();
courseController.clear();
selectedId = null;
});
},
),
future: DatabaseHelper.instance.getStudentDetails(), will get recall the api on every setState(build).
To avoid recalling the api, create a state variable for this future on state class (outside the build method).
late final future = DatabaseHelper.instance.getStudentDetails();
and use it on FutureBuilder
FutureBuilder<List<Student>>(
future: future,
builder: (BuildContext context,
You can check Fixing a common FutureBuilder and StreamBuilder problem by
Randal L. Schwartz.

Is there a way in flutter to provide the Type for Consumer<T> from parameter?

My problem in summary: I have an app, that has 3-4 master data lists which are stored in SQLite. These list are identical so I could reuse most of the widgets that build up the different master data screens. The problem is that the data is coming from different providers and I cannot find the way to extract these widgets.
This is the part where I construct the list of FishTypes using the Consumer. Can I reuse this somehow for DogTypes, CatTypes etc. without copying the whole code?
Expanded(
child: Container(
child: FutureBuilder(
future: _items,
builder: (ctx, snapshot) =>
snapshot.connectionState == ConnectionState.waiting
? Center(
child: CircularProgressIndicator(),
)
: Consumer<FishTypes>(
builder: (ctx, fishType, _) => ListView.builder(
itemCount: fishType.items.length,
itemBuilder: (ctx, i) =>
ChangeNotifierProvider.value(
value: fishType.items[i],
child: MasterDataMenuItem(),
),
),
),
),
),
),
The MasterDataMenuItem widget is basically a ListTile, where I also use Consumer to rebuild the widget when Favorite or Active switch is toggled. This is again a widget that I'd like to reuse with other Consumer types.
class MasterDataMenuItem extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 0, vertical: 5),
child: Consumer<FishType>(
builder: (_, fishType, __) => ListTile(
tileColor: Colors.black54,
leading: IconButton(
icon: Icon(fishType.isFavorite ? Icons.star : Icons.star_border),
onPressed: () {
fishType.toggleFavorite();
},
),
title: Text(
fishType.name,
textAlign: TextAlign.center,
),
trailing: fishType.isUsed
? Switch(
value: fishType.isActive,
onChanged: (_) {
fishType.toggleActive();
},
)
: IconButton(
onPressed: () {
Provider.of<FishTypes>(context, listen: false)
.deleteFishType(fishType.id!);
},
icon: Icon(Icons.delete),
),
),
),
);
}
}
Is it possible without copy / paste -ing the whole code and only change the Consumer types?
Note: there are some other parts on the screens such as title, and a text input field, but these are the parts that cause me most of the headache so I copied here only these parts.
Thanks for the help in advance!

Separating main screen with different ListView items

So I have this ListView where it creates a new ListTile item on click of a button (for now it is a floating action button, but it would need to be changed). Here is the code of the ListView:
body: Container(
),
),
child: BlocConsumer<FoodBloc, List<Food>>(
builder: (context, foodList) {
return ListView.separated(
itemBuilder: (BuildContext context, int index) {
print("foodList: $foodList");
Food food = foodList[index];
return Container(
color: Colors.white60,
child: ListTile(
trailing: Container(
padding: EdgeInsets.only(top: 15.0),
child: IconButton(
icon: Icon(Icons.add),
onPressed: () => null,
),
),
title: Text(food.name, style: TextStyle(fontSize: 30)),
subtitle: Text(
"Calories: ${food.calories} ${food.price} din\nVegan: ${food.isVegan}",
style: TextStyle(fontSize: 20),
),
onTap: () => showFoodDialog(context, food, index)),
);
},
itemCount: foodList.length,
separatorBuilder: (BuildContext context, int index) =>
Divider(color: Colors.black),
);
},
listener: (BuildContext context, foodList) {},
),
),
Here is the floating action button below that creates a new ListTile inside. I would want to have this button functionality besides every type of food like in the image example instead of floating action button (that's not a problem, the layout is):
floatingActionButton: FloatingActionButton(
tooltip: 'Add Food',
child: Icon(Icons.add),
onPressed: () => Navigator.push(context,
MaterialPageRoute(builder: (BuildContext context) => FoodForm())),
),
What I would need to do is have the same functionality, but separate the main screen with different types of food (like in the example image). They would all need to be infinitely tileable (to automatically scroll down depending on how much items there are) and would have icon button besides the type (ie drinks) that would add the ListTile to that Column. Here is a rough example of what I have and what I need.
Here is the 'Submit' button that puts the item on the ListView builder in a ListTile, not sure if anything should be changed there:
RaisedButton(
child: Text(
'Submit',
style: TextStyle(color: Colors.blue, fontSize: 16),
),
onPressed: () {
if (!_formKey.currentState.validate()) {
return;
}
_formKey.currentState.save();
Food food = Food(
name: _name,
calories: _calories,
isVegan: _isVegan,
//new var
price: _price,
//added description
description: _description,
);
DatabaseProvider.db.insert(food).then(
(storedFood) =>
BlocProvider.of<FoodBloc>(context).add(
AddFood(storedFood),
),
);

Flutter Show Data Once in ListView

I have listview in my app and in this listView I pull the book titles with API. Book titles are coming up without any problems. But if I press the button more than once, the titles increase as much as I press the button
Here is my code sample
_showData
? Container(
height: MediaQuery.of(context).size.height / 2,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
children: [
IconButton(
icon: Icon(Icons.close),
onPressed: () {
Navigator.pushNamed(
context, CountryScreen.routeName);
}),
Center(
child: Text(
'Please Select Book',
style: TextStyle(
fontWeight: FontWeight.bold, fontSize: 24),
),
),
],
),
Expanded(
child: ListView.builder(
itemCount: bookList.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(
onTap: () {
Navigator.pushNamed(
context, MainScreen.routeName);
},
title: Text(bookList[index]),
);
),
],
),
),
)
: SizedBox()
I'm calling my data here,I'm calling in the button
else {
_showData = !_showData;
books.forEach((element) {
bookList.add(element.companyName);
book.setStringList(
'bookName', bookList);
});
}
To illustrate with a small example When I click once on the button I call the data
but if I click twice I see this (the more I click the more it gets), any idea?
The build() method is called whenever there is a change to redraw in the UI. But at that time your bookList state will not be reset.
I will give a trick code to fix this problem:
bookList = [];
books.forEach((element) {
bookList.add(element.companyName);
book.setStringList('bookName', bookList);
});

Add onTap to CheckboxListTile to call AlertDialog

I am building a Flutter mobile application for taking attendance.
I am using CheckboxListTile for displaying enrollment list. I would like to call Alert Dialog when I click or tap a list item. However, I find that CheckboxListTile does not have onTap property.
So is there any way to add onTap to CheckboxListTile? Thanks!
A part of my code for creating CheckboxListTile:
Widget staffList(AsyncSnapshot<EnrollmentListModel> snapshot) {
if (snapshot.data.staffList.length > 0) {
return ListView.builder(
shrinkWrap: true,
itemCount: snapshot.data.staffList.length,
itemBuilder: (context, index) {
return Card(
child: CheckboxListTile(
title: Text(
'${snapshot.data.staffList[index].listName}',
style: TextStyle(fontSize: 16.0, fontWeight: FontWeight.bold),
),
value: snapshot.data.staffList[index].attendanceStatus == 'Y',
onChanged: (bool value) {},
activeColor: Colors.green,
),
);
},
);
}
else {
return Center(child: Text("No enrollment for this course."));
}
}
The reason onTap is not working is because the checkboxListTile itself is clickable and if we try to apply onTap on it again, it'll conflict the original click event. So instead of wrapping your checkboxListTile in GestureDetector, you can directly pass the call to open alertDialog in your onChanged method after you set the value upon clicking on it. Sample working code below:
return Scaffold(
appBar: new AppBar(title: new Text('CheckboxListTile demo')),
body: ListView(
children: values.keys.map((String key) {
return CheckboxListTile(
title: Text(key),
value: values[key],
onChanged: (bool value) {
setState(() {
values[key] = value;
});
_showDialog();
},
);
}).toList(),
),
);
}
void _showDialog() {
showDialog(
context: context,
builder: (BuildContext context) {
// return object of type Dialog
return AlertDialog(
title: new Text("Alert Dialog title"),
content: new Text("Alert Dialog body"),
actions: <Widget>[
// usually buttons at the bottom of the dialog
new FlatButton(
child: new Text("Close"),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
});
The output is, if you tap on foo checkbox, it'll check the box and will open alertDialog. Hope this answers your question.
You can wrap the CheckboxListTile inside a GestureDetector, which has a onTap property along with many others which you might find useful.
More documentation about the GestureDetector class can be found here.
Example -
Widget staffList(AsyncSnapshot<EnrollmentListModel> snapshot) {
if (snapshot.data.staffList.length > 0) {
return ListView.builder(
shrinkWrap: true,
itemCount: snapshot.data.staffList.length,
itemBuilder: (context, index) {
return Card(
child: GestureDetector(
onTap: () {
//Add code for showing AlertDialog here
},
child: CheckboxListTile(
title: Text(
'${snapshot.data.staffList[index].listName}',
style: TextStyle(fontSize: 16.0, fontWeight: FontWeight.bold),
),
value: snapshot.data.staffList[index].attendanceStatus == 'Y',
onChanged: (bool value) {},
activeColor: Colors.green,
),
),
);
},
);
}
else {
return Center(child: Text("No enrollment for this course."));
}
}
Alternative -
You can also use the InkWell widget if you only need the onTap callback. Just replace the GestureDetector in above code with InkWell.
Documentation about InkWell can be found here.
body: ListView.builder(
itemBuilder: (BuildContext context, int index) {
return GestureDetector(
child: Text(index.toString()),
onTap: () {//Write your AlertDialog code here},
);
},
itemCount: 10));