Can someone show me what i am missing in my code? I am trying to display sub-category as a dropdown in category screen in flutter using extension tile - flutter

can someone help me figure out what i'm doing wrong? I have a category screen and I have a list-view of all my parent categories on the left and my main categories filling the remaining space as a drop-down widget to display the sub-categories but the expansion tile is not opening. Below is the code of my category screen
`
import 'package:buyfast/Widget/category/main_category_widget.dart';
import 'package:buyfast/models/category_model.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:firebase_ui_firestore/firebase_ui_firestore.dart';
import 'package:flutter/material.dart';
import 'package:flutter_iconly/flutter_iconly.dart';
class CategoryScreen extends StatefulWidget {
const CategoryScreen({Key? key}) : super(key: key);
#override
State<CategoryScreen> createState() => _CategoryScreenState();
}
class _CategoryScreenState extends State<CategoryScreen> {
String _title = 'Categories';
String? selectedCategory;
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: Text(
selectedCategory==null ? _title : selectedCategory!,
style: const TextStyle(color: Colors.black,fontSize: 16),),
elevation: 0,
backgroundColor: Colors.white,
iconTheme: const IconThemeData(
color: Colors.black54
),
actions: [
IconButton(
onPressed: (){},
icon: const Icon(IconlyLight.search),
),
IconButton(
onPressed: (){},
icon: const Icon(IconlyLight.buy),
),
IconButton(
onPressed: (){},
icon: const Icon(Icons.more_vert),
),
],
),
body: Row(
children: [
Container(
width: 80,
color: Colors.grey.shade300,
child: FirestoreListView<Category>(
query: categoryCollection,
itemBuilder: (context, snapshot) {
Category category = snapshot.data();
return InkWell(
onTap: (){
setState(() {
_title= category.catName!;
selectedCategory = category.catName;
});
},
child: Container(
height: 70,
color: selectedCategory == category.catName ? Colors.white : Colors.grey.shade300,
child: Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(
height: 30,
child: CachedNetworkImage(
imageUrl: category.image!,
color: selectedCategory == category.catName ? Theme.of(context).primaryColor:Colors.grey.shade700,
),
),
Text(
category.catName!,
style: TextStyle(
fontSize: 10,
color: selectedCategory == category.catName ? Theme.of(context).primaryColor:Colors.grey.shade700,
),
textAlign: TextAlign.center,
),
],
),
),
),
),
);
},
),
),
MainCategoryWidget(
selectedCat: selectedCategory,
)
],
),
);
}
}
Now my category model to retrieve the categories from Firebase
import 'package:buyfast/firebase_service.dart';
class Category {
Category({this.catName, this.image});
Category.fromJson(Map<String, Object?> json)
: this(
catName: json['catName']! as String,
image: json['image']! as String,
);
final String? catName;
final String? image;
Map<String, Object?> toJson() {
return {
'catName': catName,
'image': image,
};
}
}
FirebaseService _service = FirebaseService();
final categoryCollection = _service.categories.where('active',isEqualTo: true).withConverter<Category>(
fromFirestore: (snapshot, _) => Category.fromJson(snapshot.data()!),
toFirestore: (category, _) => category.toJson(),
);
My category widget
import 'package:buyfast/models/category_model.dart';
import 'package:firebase_ui_firestore/firebase_ui_firestore.dart';
import 'package:flutter/material.dart';
import 'package:flutter_iconly/flutter_iconly.dart';
class CategoryWidget extends StatefulWidget {
const CategoryWidget({Key? key}) : super(key: key);
#override
State<CategoryWidget> createState() => _CategoryWidgetState();
}
class _CategoryWidgetState extends State<CategoryWidget> {
String? _selectedCategory;
#override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: Column(
children: [
const SizedBox(height: 18,),
const Padding(
padding: EdgeInsets.all(8.0),
child: Align(
alignment: Alignment.centerLeft,
child: Text(
'Stores For You',
style: TextStyle(
fontWeight: FontWeight.bold,
letterSpacing: 1,
fontSize: 20
),
),
),
),
Padding(
padding: const EdgeInsets.fromLTRB(8,0,8,8),
child: SizedBox(
height: 40,
child: Row(
children: [
Expanded(
child:FirestoreListView<Category>(
scrollDirection: Axis.horizontal,
query: categoryCollection,
itemBuilder: (context, snapshot) {
Category category = snapshot.data();
return Padding(
padding: const EdgeInsets.only(right: 4),
child: ActionChip(
padding: EdgeInsets.zero,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(2)
),
backgroundColor: _selectedCategory == category.catName ? Colors.blue.shade900 : Colors.grey,
label: Text(
category.catName!,
style: TextStyle(
fontSize: 12,
color: _selectedCategory==category.catName ? Colors.white : Colors.black
),
),
onPressed: () {
setState(() {
_selectedCategory = category.catName;
});
},
),
);
},
),
),
Container(
decoration: BoxDecoration(
border: Border(left: BorderSide(color: Colors.grey.shade400),)
),
child: IconButton(
onPressed: (){
},
icon: const Icon(IconlyLight.arrowDown),
),
)
],
),
),
),
],
),
);
}
}
main category widget
import 'package:buyfast/Widget/category/sub_category_widget.dart';
import 'package:buyfast/models/main_category_model.dart';
import 'package:firebase_ui_firestore/firebase_ui_firestore.dart';
import 'package:flutter/material.dart';
class MainCategoryWidget extends StatefulWidget {
final String? selectedCat;
const MainCategoryWidget({this.selectedCat,Key? key}) : super(key: key);
#override
State<MainCategoryWidget> createState() => _MainCategoryWidgetState();
}
class _MainCategoryWidgetState extends State<MainCategoryWidget> {
#override
Widget build(BuildContext context) {
return Expanded(
child: FirestoreListView<MainCategory>(
query: mainCategoryCollection(widget.selectedCat),
itemBuilder: (context, snapshot) {
MainCategory mainCategory = snapshot.data();
return ExpansionTile(
title: Text(mainCategory.mainCategory!),
children: [
SubCategoryWidget(
selectedSubCat: mainCategory.mainCategory,
)
],
);
},
),
);
}
}
main category model
import 'package:buyfast/firebase_service.dart';
class MainCategory {
MainCategory({this.category, this.mainCategory});
MainCategory.fromJson(Map<String, Object?> json)
: this(
category: json['category']! as String,
mainCategory: json['mainCategory']! as String,
);
final String? category;
final String? mainCategory;
Map<String, Object?> toJson() {
return {
'category': category,
'mainCategory': mainCategory,
};
}
}
FirebaseService _service = FirebaseService();
mainCategoryCollection (selectedCat){
return _service.mainCategories.where('approved',isEqualTo: true).where('category', isEqualTo: selectedCat).withConverter<MainCategory>(
fromFirestore: (snapshot, _) => MainCategory.fromJson(snapshot.data()!),
toFirestore: (category, _) => category.toJson(),);
}
`
Subcategory model
`
import 'package:buyfast/firebase_service.dart';
class SubCategory {
SubCategory({this.mainCategory, this.subCatName, this.image});
SubCategory.fromJson(Map<String, Object?> json)
: this(
mainCategory: json['mainCategory']! as String,
subCatName: json['subCatName']! as String,
image: json['image']! as String,
);
final String? mainCategory;
final String? subCatName;
final String? image;
Map<String, Object?> toJson() {
return {
'mainCategory': mainCategory,
'subCatName': subCatName,
'image': image,
};
}
}
FirebaseService _service = FirebaseService();
subCategoryCollection({selectedSubCat}){
return _service.subCategories.where('active',isEqualTo: true).where('mainCategory',isEqualTo: selectedSubCat).withConverter<SubCategory>(
fromFirestore: (snapshot, _) => SubCategory.fromJson(snapshot.data()!),
toFirestore: (category, _) => category.toJson(),
);
}
`
Subcategory widget
`
import 'package:buyfast/models/sub_category_model.dart';
import 'package:firebase_ui_firestore/firebase_ui_firestore.dart';
import 'package:flutter/material.dart';
class SubCategoryWidget extends StatelessWidget {
final String? selectedSubCat;
const SubCategoryWidget({this.selectedSubCat,Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Expanded(
child: FirestoreQueryBuilder<SubCategory>(
query: subCategoryCollection(
selectedSubCat: selectedSubCat
),
builder: (context, snapshot, _) {
if (snapshot.isFetching) {
return const Center(child: CircularProgressIndicator());
}
if (snapshot.hasError) {
return Text('Something went wrong! ${snapshot.error}');
}
return GridView.builder(
shrinkWrap: true,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
childAspectRatio: snapshot.docs.length == 0 ? 1/.1 : 1/1.1,
),
itemCount: snapshot.docs.length,
itemBuilder: (context, index) {
SubCategory subCat = snapshot.docs[index].data();
return InkWell(
onTap: (){
//move to product screen
},
child: Column(
children: [
SizedBox(
height: 60,
width: 60,
child: FittedBox(
fit: BoxFit.contain,
child: Image.network(subCat.image!)),
),
Text(subCat.subCatName!,style: const TextStyle(fontSize: 12),
textAlign: TextAlign.center,
),
],
),
);
},
);
},
),
);
}
}
`

I solved it by wrapping the Expanded widget in the sub_category_widget.dart file with a SizeBox and give it a height of 100.
Like below.
import 'package:buyfast/models/sub_category_model.dart';
import 'package:firebase_ui_firestore/firebase_ui_firestore.dart';
import 'package:flutter/material.dart';
class SubCategoryWidget extends StatelessWidget {
final String? selectedSubCat;
const SubCategoryWidget({this.selectedSubCat,Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return SizeBox(height: 100,
Expanded(
child: FirestoreQueryBuilder<SubCategory>(
query: subCategoryCollection(
selectedSubCat: selectedSubCat
),
builder: (context, snapshot, _) {
if (snapshot.isFetching) {
return const Center(child: CircularProgressIndicator());
}
if (snapshot.hasError) {
return Text('Something went wrong! ${snapshot.error}');
}
return GridView.builder(
shrinkWrap: true,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
childAspectRatio: snapshot.docs.length == 0 ? 1/.1 : 1/1.1,
),
itemCount: snapshot.docs.length,
itemBuilder: (context, index) {
SubCategory subCat = snapshot.docs[index].data();
return InkWell(
onTap: (){
//move to product screen
},
child: Column(
children: [
SizedBox(
height: 60,
width: 60,
child: FittedBox(
fit: BoxFit.contain,
child: Image.network(subCat.image!)),
),
Text(subCat.subCatName!,style: const TextStyle(fontSize: 12),
textAlign: TextAlign.center,
),
],
),
);
},
);
},
),
),
);
}
}

Related

Deleting Item out of List, Listview.build shows wrong data

I have a Stateful widget that i pass a list to (for example 2 items).
After I delete an item, the widget should rebuild itself.
Unfortunately, the deleted item is still displayed and the other one is not.
When I re-enter the widget, the correct item is loaded.
There is a similar problem List not updating on deleting item
but maybe someone can explain me what i did wrong and why provider is helping me here instead of setState?
My code is:
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter/material.dart';
import 'package:trip_planner/util/dialog_box.dart';
import 'package:trip_planner/util/previewUrl.dart';
class BookingPage extends StatefulWidget {
final List toDoList;
BookingPage({
super.key,
required this.toDoList,
});
#override
State<BookingPage> createState() => _BookingPageState();
}
class _BookingPageState extends State<BookingPage> {
//text controller
final _controller = TextEditingController();
final _database = FirebaseDatabase.instance.ref();
//Liste is an example what i have in my list
List toDoList2 = [
["https://www.booking.com/Share-Rnv2Kf", true],
["https://www.booking.com/Share-3hKQ0r", true],
];
void initState(){
super.initState();
}
void deleteTask(int index){
setState(() {
widget.toDoList.removeAt(index);
});
//DatabaseReference _testRef = _database.child("Hotel:");
//_testRef.set(widget.toDoList.toString());
}
//save new Item
void saveNewItem(){
setState(() {
widget.toDoList.add([_controller.text, false]);
//DatabaseReference _testRef = _database.child("Hotel:");
//_testRef.set(widget.toDoList.toString());
_controller.clear();
});
Navigator.of(context).pop();
}
void createNewItem(){
showDialog(
context: context,
builder: (context){
return DialogBox(
controller: _controller,
onSave: saveNewItem,
onCancel: () => Navigator.of(context).pop(),
);
},
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Booking Seiten'),
elevation: 0,
),
floatingActionButton: FloatingActionButton(
onPressed: createNewItem,
child: Icon(Icons.add),
),
body: ListView.builder(
itemCount: widget.toDoList.length,
itemBuilder: (context, index){
return PreviewUrl(
url2: widget.toDoList[index][0],
deleteFunction: (context) => setState(() => deleteTask(index)),
);
},
),
);
}
}
i thought setState does the same thing as when i re-enter the widget, but it doesn't.
import 'package:any_link_preview/any_link_preview.dart';
import 'package:flutter/material.dart';
import 'package:flutter_slidable/flutter_slidable.dart';
import 'package:url_launcher/url_launcher.dart';
class PreviewUrl extends StatelessWidget {
final String url2;
//Function(bool?)? onChanged;
Function(BuildContext)? deleteFunction;
PreviewUrl({
super.key,
required this.url2,
required this.deleteFunction,
//required this.onChanged,
});
Future openBrowserURL({
required String url,
bool inApp = false,
}) async {
if(await canLaunch(url)){
await launch(
url,
forceSafariVC: inApp, //iOS
forceWebView: inApp, //Android
enableJavaScript: true, //Android
);
}
}
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(1.0),
child: Slidable(
endActionPane: ActionPane(
motion: StretchMotion(),
children: [
SlidableAction(
onPressed: deleteFunction,
icon: Icons.delete,
backgroundColor: Colors.red.shade300,
borderRadius: BorderRadius.circular(12),
)
],
),
child: Container(
child: AnyLinkPreview.builder(
link: url2,
itemBuilder: (context, metadata, imageProvider) => Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (imageProvider != null)
GestureDetector(
onTap: () async {
final url = url2;
openBrowserURL(url: url, inApp: true);
},
child: Container(
constraints: BoxConstraints(
maxHeight: MediaQuery.of(context).size.width *0.25,
),
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(12),
topRight: Radius.circular(12)),
image: DecorationImage(
image: imageProvider,
fit: BoxFit.cover,
),
),
),
),
Container(
width: double.infinity,
color: Theme.of(context).primaryColor.withOpacity(0.6),
padding: const EdgeInsets.symmetric(
vertical: 10, horizontal: 15),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (metadata.title != null)
Text(
metadata.title!,
maxLines: 1,
style:
const TextStyle(fontWeight: FontWeight.w500),
),
const SizedBox(height: 5),
if (metadata.desc != null)
Text(
metadata.desc!,
maxLines: 1,
style: Theme.of(context).textTheme.bodySmall,
),
Text(
metadata.url ?? url2,
maxLines: 1,
style: Theme.of(context).textTheme.bodySmall,
),
],
),
),
],
),
),
),
),
);
}
}
If you run the simplified version of your code in DartPad - it will work:
import 'package:flutter/material.dart';
const Color darkBlue = Color.fromARGB(255, 18, 32, 47);
List toDoList = [
["Button 1", true],
["Button 2", true],
];
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: darkBlue,
),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: BookingPage(toDoList: toDoList),
),
),
);
}
}
class BookingPage extends StatefulWidget {
final List toDoList;
const BookingPage({
super.key,
required this.toDoList,
});
#override
State<BookingPage> createState() => _BookingPageState();
}
class _BookingPageState extends State<BookingPage> {
//Liste is an example what i have in my list
List toDoList2 = [
["Button 1", true],
["Button 2", true],
];
#override
void initState() {
super.initState();
}
void deleteTask(int index) {
setState(() {
widget.toDoList.removeAt(index);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Booking Seiten'),
elevation: 0,
),
body: ListView.builder(
itemCount: widget.toDoList.length,
itemBuilder: (context, index) {
return ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.lightBlue,
padding: const EdgeInsets.all(12),
textStyle: const TextStyle(fontSize: 22),
),
child: Text(widget.toDoList[index][0]!),
onPressed: () => setState(() => deleteTask(index)),
);
},
),
);
}
}
Which tells me that the problem is your PreviewUrl. My guess is - it is a statful widget, and when the tree rebuilds - it will link the old State object to the first item.
Using Keys might help, something like:
return PreviewUrl(
key: ObjectKey(widget.toDoList[index]),
url2: widget.toDoList[index][0],
deleteFunction: (context) => setState(() => deleteTask(index)),
);

Flutter - Provider - Clearing Data itself when navigate

When I in a page and added some quantity and navigate to another page and come back to added more quantity data is cleared.
Here's my code.
add_inv_stream.dart
class AddInvStream extends StatelessWidget {
const AddInvStream({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
FirebaseServices services = FirebaseServices();
final provider = Provider.of<InventoryProvider>(context);
return StreamBuilder<QuerySnapshot>(
stream: services.inventory
.where('fishType', isEqualTo: provider.inventoryData['fishType'])
.where('status', isEqualTo: true)
.snapshots(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return const Center(child: Text('Something wrong!'));
}
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
}
if (snapshot.data!.size == 0) {
return const Center(
child: Text('No inventories'),
);
}
return AddInvData(snapshot: snapshot, services: services);
},
);
}
}
add_inv_data.dart
class AddInvData extends StatefulWidget {
final AsyncSnapshot<QuerySnapshot<Object?>> snapshot;
final FirebaseServices services;
const AddInvData({Key? key, required this.snapshot, required this.services})
: super(key: key);
#override
State<AddInvData> createState() => _AddInvDataState();
}
class _AddInvDataState extends State<AddInvData> {
final _qty = TextEditingController();
List sellerList = [];
#override
Widget build(BuildContext context) {
final providerr = Provider.of<InventoryProvider>(context);
return ListView.builder(
padding: const EdgeInsets.all(15.0),
physics: const ScrollPhysics(),
shrinkWrap: true,
itemCount: widget.snapshot.data!.size,
itemBuilder: (context, index) {
Map<String, dynamic> sellerData =
widget.snapshot.data!.docs[index].data() as Map<String, dynamic>;
int sellerQty = int.parse(sellerData['qty']);
return InkWell(
onTap: () {
showDialog(
context: context,
useRootNavigator: false,
builder: (context) {
return Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(40),
),
elevation: 16,
child: Container(
padding: const EdgeInsets.all(20),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text('Quantity in kg: '),
const SizedBox(width: 10),
Container(
height: 60,
width: 60,
decoration: BoxDecoration(
border: Border.all(color: Colors.black),
),
child: TextFormField(
controller: _qty,
textAlign: TextAlign.center,
keyboardType: TextInputType.number,
decoration: const InputDecoration(
border: InputBorder.none,
),
),
),
],
),
const SizedBox(height: 50),
TextButton(
onPressed: () async {
Map<String, dynamic> seller = {
'date': sellerData['date'],
'qty': _qty.text,
};
sellerList.add(seller);
providerr.getData(sellerList: sellerList);
print(providerr.inventoryData['sellerList']);
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => const InvDetails(),
));
},
child: const Text('ASSIGN'),
),
],
),
),
);
},
);
},
child: Padding(
padding: const EdgeInsets.fromLTRB(10, 0, 10, 30),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
sellerData['date'],
style: const TextStyle(fontSize: 18),
),
Text(
'${sellerData['qty']} kg',
style: const TextStyle(fontSize: 18),
),
],
),
),
);
},
);
}
}
inv_details.dart
class InvDetails extends StatelessWidget {
const InvDetails({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: TextButton(
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => const AddInv(),
));
},
child: const Text('Add Inventory'),
),
),
);
}
}
inv_provider.dart
class InventoryProvider with ChangeNotifier {
Map<String, dynamic> inventoryData = {};
getData({List? sellerList}) {
if (sellerList != null) {
inventoryData['sellerList'] = sellerList;
}
notifyListeners();
}
}
Here printing at the first
[{date: 10/31/2022, qty: 1}]
When I navigate to InvDetails from AddInvData and when the button is clicked it's printing
[{date: 11/25/2022, qty: 1}]
Data is not updating
But if I didn't navigate.
i.e If I stay in AddInvData and added quantity it is updating.
inv_data.dart (Added textbutton part only ) removed navigate
TextButton(
onPressed: () async {
Map<String, dynamic> seller = {
'date': sellerData['date'],
'qty': _qty.text,
};
sellerList.add(seller);
providerr.getData(sellerList: sellerList);
print(providerr.inventoryData['sellerList']);
},
prints
[{date: 10/31/2022, qty: 1}]
[{date: 10/31/2022, qty: 1}, {date: 11/25/2022, qty: 1}]
Why this is not hapenning when navigate and come back to previous page?

Flutter : Adding item from one list view to another list view

I am trying to select one item from phone contacts list (List view widget)
class PhoneContacts extends StatefulWidget {
const PhoneContacts({Key? key}) : super(key: key);
#override
State<PhoneContacts> createState() => _PhoneContactsState();
}
class _PhoneContactsState extends State<PhoneContacts> {
List<Contact> _contacts = [];
late PermissionStatus _permissionStatus;
late Customer _customer;
#override
void initState(){
super.initState();
getAllContacts();
}
void getAllContacts() async {
_permissionStatus = await Permission.contacts.request();
if(_permissionStatus.isGranted) {
List<Contact> contacts = await ContactsService.getContacts(withThumbnails: false);
setState(() {
_contacts = contacts;
});
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Phone Contacts"),
backgroundColor: Colors.indigo[600],
),
body: Container(
padding: const EdgeInsets.all(5),
child: ListView.builder(
itemCount: _contacts.length,
itemBuilder: (BuildContext context, int index) {
Contact contact = _contacts[index];
return contactItem(contact);
}
),
),
);
}
Widget contactItem(Contact contact){
return ListTile(
onTap: () {
Navigator.of(context).push(MaterialPageRoute(builder: (context)=>Dashboard(contact)));
},
leading: const CircleAvatar(
backgroundColor: Colors.pinkAccent,
child: Icon(Icons.person_outline_outlined)),
title : Text(contact.displayName.toString()),
subtitle: Text(contact.phones!.first.value.toString()),
);
}
}
and insert and display it to dashboard list (another List view widget)
class Dashboard extends StatefulWidget {
final Contact? contact;
const Dashboard([this.contact]);
#override
State<Dashboard> createState() => _DashboardState();
}
class _DashboardState extends State<Dashboard> {
final Color? themeColor = Colors.indigo[600];
late GlobalKey<RefreshIndicatorState> refreshKey;
late List<CardGenerator> existingCustomerContactList = getCustomerContactList();
#override
void initState(){
super.initState();
refreshKey=GlobalKey<RefreshIndicatorState>();
}
void addCustomerContact() {
existingCustomerContactList.add(
CardGenerator(
Text(widget.contact!.displayName.toString()),
const Icon(Icons.account_circle),
Text(widget.contact!.phones!.first.value.toString())));
}
List<CardGenerator> getCustomerContactList () {
existingCustomerContactList = [
CardGenerator(
const Text('Dave', style: TextStyle(fontSize: 24.0), textAlign: TextAlign.start,),
const Icon(Icons.account_circle, size: 100, color: Colors.white,),
const Text('Address 1')),
CardGenerator(
const Text('John', style: TextStyle(fontSize: 24.0)),
const Icon(Icons.account_circle, size: 100, color: Colors.white),
const Text('Address 2')),
CardGenerator(
const Text('Richard', style: TextStyle(fontSize: 24.0)),
const Icon(Icons.account_circle, size: 100, color: Colors.white),
const Text('Address 3')),
];
return existingCustomerContactList;
}
Future<void> refreshList() async {
await Future.delayed(const Duration(seconds: 1));
setState(() => {
addCustomerContact(),
getCustomerContactList()
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[50],
appBar: AppBar(
title: const Text("Dashboard"),
backgroundColor: themeColor,
),
body: RefreshIndicator(
key: refreshKey,
onRefresh: () async {
await refreshList();
},
child: Column(
children: [
Expanded(
child: ListView.builder(
itemCount: existingCustomerContactList.length,
key: UniqueKey(),
itemBuilder: (BuildContext context, int index) {
return OpenContainer(
closedColor: Colors.transparent,
closedElevation: 0.0,
openColor: Colors.transparent,
openElevation: 0.0,
transitionType: ContainerTransitionType.fadeThrough,
closedBuilder: (BuildContext _, VoidCallback openContainer) {
return Card(
color: Colors.white,
child: GestureDetector(
onTap: openContainer,
child: SizedBox(
height: 140,
child: Row(
children: [
Container(
decoration: const BoxDecoration(
color: Colors.indigo,
borderRadius: BorderRadius.only(topLeft: Radius.circular(7.0),bottomLeft: Radius.circular(7.0))
),
height: 140,
width: 120,
child: existingCustomerContactList[index].icon,
),
Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: existingCustomerContactList[index].title,
),
Padding(
padding: const EdgeInsets.all(8.0),
child: existingCustomerContactList[index].address,
),
],
)
],
),
),
),
);
},
openBuilder: (BuildContext _, VoidCallback openContainer) {
return ConsumerHome();
}
);
}),
),
],
),
),
);
}
}
I found the
selected item has been added to the Dashboard items list but when I refresh it it doesn't newly added item in the dashboard list view.
I am a newcomer in flutter please bare with me. I already did my search for this problem unfortunately, no luck.
Change the order of execution. You are adding the item in the list and then making a new list again in the current order
addCustomerContact(),
getCustomerContactList()
change this to
getCustomerContactList()
addCustomerContact(),

Why does a Container in a SingleChildScrollView not constrain content?

I'm pretty new both to Flutter/Dart and Stack Overflow. I started with some trip ETA code and modified it to be an initiative turn tracker for a fantasy RPG tabletop game.
My intent is to keep the End Encounter button on the screen at all times, and have the timeline content scroll in a window above it.
I added a SingleChildScrollView into _TurnSectionState at line 178, but as soon as I add a Container into it I get the overflow. I tried for about 3 hours before asking for help here.
final data = _data(1);
return Scaffold(
appBar: TitleAppBar(widget._title),
body: Center(
child: SingleChildScrollView(
child: SizedBox(
width: 380.0,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: const EdgeInsets.all(20.0),
child: _MissionName(
timeSliceInfo: data,
),
),
const Divider(height: 1.0),
_TurnSection(processes: data.deliveryProcesses),
const Divider(height: 1.0),
const Padding(
padding: EdgeInsets.all(20.0),
child: _OnTimeBar(),
),
],
),
),
),
),
);
}
}
// Mission name
class _MissionName extends StatelessWidget {
const _MissionName({
Key? key,
required this.timeSliceInfo,
}) : super(key: key);
final _TimeSliceInfo timeSliceInfo;
#override
Widget build(BuildContext context) {
return Row(
children: [
Text(
missions[currentMission].getTitle(),
style: const TextStyle(
fontWeight: FontWeight.bold,
),
),
],
);
}
}
// Lists items in each turn
class _InnerTimeline extends StatefulWidget {
const _InnerTimeline({
required this.messages,
});
final List<ActivationLineItem> messages;
#override
State<_InnerTimeline> createState() => _InnerTimelinePageState();
}
class _InnerTimelinePageState extends State<_InnerTimeline> {
#override
initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
bool isEdgeIndex(int index) {
return index == 0 || index == widget.messages.length + 1;
}
// Interior connector lines
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: FixedTimeline.tileBuilder(
theme: TimelineTheme.of(context).copyWith(
nodePosition: 0,
connectorTheme: TimelineTheme.of(context).connectorTheme.copyWith(
thickness: 1.0,
),
indicatorTheme: TimelineTheme.of(context).indicatorTheme.copyWith(
size: 10.0,
position: 0.5,
),
),
builder: TimelineTileBuilder(
indicatorBuilder: (_, index) =>
!isEdgeIndex(index) ? Indicator.outlined(borderWidth: 1.0) : null,
startConnectorBuilder: (_, index) => Connector.solidLine(),
endConnectorBuilder: (_, index) => Connector.solidLine(),
contentsBuilder: (_, index) {
if (isEdgeIndex(index)) {
return null;
}
// Line item (init value + icon + text)
return Padding(
padding: const EdgeInsets.only(left: 8.0),
child: Row(children: [
Text(widget.messages[index - 1].tick.toString()),
const SizedBox(width: 6),
Image(
image: AssetImage(widget.messages[index - 1].thumbnail),
width: 20,
height: 20),
const SizedBox(width: 6),
Expanded(
child: Text(widget.messages[index - 1].message,
overflow: TextOverflow.ellipsis, maxLines: 1))
]));
},
itemExtentBuilder: (_, index) => isEdgeIndex(index) ? 10.0 : 30.0,
nodeItemOverlapBuilder: (_, index) =>
isEdgeIndex(index) ? true : null,
itemCount: widget.messages.length + 2,
),
),
);
}
}
// Outer timeline (List of turns)
class _TurnSection extends StatefulWidget {
const _TurnSection({Key? key, required processes})
: _processes = processes,
super(key: key);
final List<TurnContents> _processes;
#override
_TurnSectionState createState() => _TurnSectionState();
}
class _TurnSectionState extends State<_TurnSection> {
bool isExpanded = false;
#override
Widget build(BuildContext context) {
return DefaultTextStyle(
style: const TextStyle(
color: Color(0xff9b9b9b), // Nobel Gray
fontSize: 12.5,
),
child: SingleChildScrollView(
child: SizedBox(
height: 480,
child: Column(
children: [
FixedTimeline.tileBuilder(
theme: TimelineThemeData(
nodePosition: 0,
color: const Color(0xff989898), // Spanish Gray
indicatorTheme: const IndicatorThemeData(
position: 0,
size: 20.0, // Outer timeline circle size
),
connectorTheme: const ConnectorThemeData(
thickness: 2.5, // Outer timeline line thickness
),
),
builder: TimelineTileBuilder.connected(
connectionDirection: ConnectionDirection.before,
itemCount: widget._processes.length,
contentsBuilder: (_, index) {
if (widget._processes[index].isCompleted) return null;
return GestureDetector(
onTap: () {
int turnNum = widget._processes[index].getTurnNum();
if (turnNum == currentTurn) {
// Ask if ready for next turn
showDialog<String>(
context: context,
builder: (BuildContext context) => AlertDialog(
title: const Text('Next Turn?'),
content: Text('Ready to start turn ?' +
(currentTurn + 1).toString()),
actions: <Widget>[
TextButton(
onPressed: () =>
Navigator.pop(context, 'Yes'),
child: const Text('Yes'),
),
TextButton(
onPressed: () => Navigator.pop(context, 'No'),
child: const Text('No'),
),
],
),
).then((value) {
if (value == 'Yes') {
currentTurn++; // Move to the next turn
setState(() {}); // Draw the screen
}
});
}
},
child: Padding(
padding: const EdgeInsets.only(left: 8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
widget._processes[index]
.getName(), // Turn name font size
style:
DefaultTextStyle.of(context).style.copyWith(
fontSize: 18.0,
),
),
_InnerTimeline(
messages:
widget._processes[index].getMessages()),
],
),
),
);
},
indicatorBuilder: (_, index) {
if (index <= currentTurn) {
return const DotIndicator(
color: Color(0xff66c97f), // Emerald Green dot
child: Icon(
Icons.check,
color: Colors.white,
size: 12.0,
),
);
} else {
return const OutlinedDotIndicator(
borderWidth: 2.5,
);
}
},
connectorBuilder: (_, index, ___) => SolidLineConnector(
color: index <= currentTurn
? const Color(0xff66c97f) // Emerald Green connector
: null, // Green
),
),
),
],
),
),
),
);
}
}
class _OnTimeBar extends StatelessWidget {
const _OnTimeBar({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Row(
children: [
MaterialButton(
onPressed: () {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('On Time!'),
),
);
},
elevation: 0,
shape: const StadiumBorder(),
color: const Color(0xff66c97f),
textColor: Colors.white,
child: const Text('End Encounter'),
),
const Spacer(),
const SizedBox(width: 12.0),
],
);
}
}
/*
List<TurnContents> buildTimeline() {
List<TurnContents> turns;
return turns;
}
*/
_TimeSliceInfo _data(int id) => _TimeSliceInfo(
id: id,
date: DateTime.now(),
deliveryProcesses: ImpulseBuilder().buildTimeline(),
);
class _TimeSliceInfo {
const _TimeSliceInfo({
required this.id,
required this.date,
required this.deliveryProcesses,
});
final int id;
final DateTime date;
final List<TurnContents> deliveryProcesses;```
}
[1]: https://i.stack.imgur.com/mGd5X.png
use Expanded and in SingleChildScrollView add physics:ScrollPhysics()
Expanded(
child: Container(
child: SingleChildScrollView(
physics: ScrollPhysics(),
child: Column(
children: []
)
)
)
);

unable to update single item background color in Listview.builder in flutter

Im working on flutter listview.builder I just want to change background colour of 3 items in Row widget at where user clicks but its changing colours of all the items in listview. just want to change color at specific index based on user click. here is my code...
import 'dart:convert';
import 'package:dotted_border/dotted_border.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:fluttergyancare/ColorLoader3.dart';
import 'package:fluttergyancare/Models/AddAttendanceModel.dart';
import 'package:fluttergyancare/Models/serializers.dart';
import 'package:gradient_app_bar/gradient_app_bar.dart';
import 'package:http/http.dart' as http;
class AddAttendance extends StatelessWidget {
final String id;
final String section;
final String school;
final String Class;
AddAttendance({this.id, this.section, this.school, this.Class});
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color(0xFFEEEEEE),
appBar: GradientAppBar(
title: Text('Normal'),
centerTitle: true,
backgroundColorStart: Color(0xFFFF9844),
backgroundColorEnd: Color(0xFFFD7267),
),
body: FutureBuilderUI(
id: id,
section: section,
school: section,
Class: Class,
),
);
}
}
Future<AddAttendanceModel> call(http.Client client, String id, String section,
String school, String Class) async {
var send =
await http.post("http://localhost/app/api/get_students", body: {
"teacher_id": "1",
"class_id": id,
"section": section,
"school_name": school,
"Class": Class,
});
return compute(parseJson, (send.body));
}
AddAttendanceModel parseJson(String json) {
final jsonStr = jsonDecode(json);
AddAttendanceModel article = standardSerializers.deserializeWith(
AddAttendanceModel.serializer, jsonStr);
return article;
}
class FutureBuilderUI extends StatelessWidget {
final String id;
final String section;
final String school;
final String Class;
FutureBuilderUI({this.id, this.section, this.school, this.Class});
#override
Widget build(BuildContext context) {
return FutureBuilder<AddAttendanceModel>(
future: call(http.Client(), id, section, school, Class),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
case ConnectionState.active:
case ConnectionState.waiting:
return Center(
child: ColorLoader3(
radius: 25.0,
dotRadius: 10.0,
),
);
case ConnectionState.done:
if (snapshot.hasError) print(snapshot.error);
print(snapshot.data.studentsInfo.length.toString() +
" StudentsInfo Length");
if (snapshot.data.studentsInfo.length != 0) {
return snapshot.hasData
? AddAttendanceUI(students: snapshot.data)
: Container();
} else {
return Container(
padding: EdgeInsets.all(10.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Align(alignment: Alignment.center, child: Text("No..")),
],
),
);
}
}
return null;
// return ListView.builder(
// itemBuilder: (context, index) {
// return ListTile(
// title: Text(snapshot.data[index].status),
// );
// },
// );
},
);
}
}
class AddAttendanceUI extends StatefulWidget {
final AddAttendanceModel students;
AddAttendanceUI({this.students});
#override
_AddAttendanceUIState createState() => _AddAttendanceUIState();
}
class _AddAttendanceUIState extends State<AddAttendanceUI> {
var pColor = Colors.green;
var aColor = Colors.grey;
var nColor = Colors.grey;
int _onSelectedindex = 0;
#override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: widget.students.studentsInfo.length,
itemBuilder: (BuildContext context, int index) {
return Card(
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
SizedBox(width: 10),
Spacer(),
Padding(
padding: const EdgeInsets.only(right: 10.0),
child: Row(
children: <Widget>[
GestureDetector(
child: pSelector(),
onTap: () {
print(index);
setState(() {
// final a = widget.students.studentsInfo
// .where((l) =>
// l.id ==
// widget.students.studentsInfo[index].id)
// .toList();
_onSelectedindex = index;
aColor = Colors.grey;
pColor = Colors.green;
nColor = Colors.grey;
});
},
),
SizedBox(width: 12),
GestureDetector(
onTap: () {
setState(() {
print(widget.students.studentsInfo[index].id);
aColor = Colors.red;
pColor = Colors.grey;
nColor = Colors.grey;
});
},
child: aSelector()),
SizedBox(width: 12),
GestureDetector(
child: nSelector(),
onTap: () {
setState(() {
print(widget.students.studentsInfo[index].id);
aColor = Colors.grey;
pColor = Colors.grey;
nColor = Colors.orange;
});
},
),
],
),
)
],
),
);
});
}
hello() {}
Widget pSelector() {
return Padding(
padding: const EdgeInsets.only(top: 5.0),
child: ClipOval(
child: Container(
color: pColor,
height: 30,
width: 30,
child: DottedBorder(
strokeWidth: 2.5,
borderType: BorderType.Circle,
color: Colors.white,
child: Center(
child: Text(
"A",
style: TextStyle(color: Colors.white),
),
),
),
),
),
);
}
Widget aSelector() {
return Padding(
padding: const EdgeInsets.only(top: 5.0),
child: ClipOval(
child: Container(
color: aColor,
height: 30,
width: 30,
child: DottedBorder(
strokeWidth: 2.5,
borderType: BorderType.Circle,
color: Colors.white,
child: Center(
child: Text(
"B",
style: TextStyle(color: Colors.white),
),
),
),
),
),
);
}
Widget nSelector() {
return Padding(
padding: const EdgeInsets.only(top: 5.0),
child: ClipOval(
child: Container(
color: nColor,
height: 30,
width: 30,
child: DottedBorder(
strokeWidth: 2.5,
borderType: BorderType.Circle,
color: Colors.white,
child: Center(
child: Text(
"C",
style: TextStyle(color: Colors.white),
),
),
),
),
),
);
}
}
see Images below image1 image2 image3
I tried to attach streamBuilder with each single item in listview but not working.
I expect when user taps on A/B/C only at that index colours should be change but its changing colours of all the items
You are using the same 3 variables pColor, aColor, nColor for all the items. You should have a list or a map of these variables, one for each item. Or create a separate widget to handle these variables internally on that widget.