I was building a todo list app which uses a ListView.builder to render multiple task widgets which have three properties, title, checked, and starred. It works just fine until I star/check an existing task and add a new task, in which case the state of the previous task seems to jump to the newly added task. What could be causing the problem? Could this have something to do with keys?
class Main extends StatefulWidget {
#override
State<Main> createState() => _MyAppState();
}
class _MyAppState extends State<Main> {
var todoList = [];
var stars = 0;
void addStar() {
setState(() {
stars++;
});
}
void removeStar() {
if (stars > 0) {
setState(() {
stars--;
});
}
}
void addTodo(title) {
setState(() {
todoList.add(title);
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
floatingActionButton: Builder(builder: (context) {
return FloatingActionButton(
child: const Icon(Icons.add),
onPressed: () {
showModalBottomSheet(
context: context,
builder: (BuildContext context) {
return NewTodo(addTodo: addTodo);
});
},
);
}),
body: SafeArea(
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(12.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
'My Day',
style:
TextStyle(fontSize: 28, fontWeight: FontWeight.bold),
),
Container(
child: Row(
children: [
const Text(
'IMPORTANT: ',
style: TextStyle(
fontSize: 16.0,
letterSpacing: 1.0,
fontWeight: FontWeight.bold,
color: Color.fromARGB(255, 58, 58, 58),
),
),
Text(
'$stars',
style: const TextStyle(
fontSize: 24.0,
letterSpacing: 1.0,
fontWeight: FontWeight.bold,
color: Color.fromARGB(255, 253, 147, 8)),
),
const SizedBox(width: 20.0),
IconButton(
icon: Icon(Icons.more_vert, size: 24),
onPressed: () => {print('more ...')},
),
],
),
)
],
),
),
Expanded(
// height: 300,
child: todoList.length == 0
? Text('No Tasks Yet 💪',
style: TextStyle(
fontSize: 24.0,
fontWeight: FontWeight.w600,
color: Colors.grey))
: ListView.builder(
itemCount: todoList.length,
itemBuilder: (context, index) {
return Todo(
title: todoList[(todoList.length - 1) - index],
addStar: addStar,
removeStar: removeStar);
}))
],
),
),
),
);
}
}
the app currently only has three files, below is a link to the gist.
https://gist.github.com/FENETMULER/eb4a898b82f9aa4c6a871a1fa9833c84
Change your logic like
: ListView.builder(
itemCount: todoList.length,
itemBuilder: (context, index) {
return Todo(
title: todoList[index], //this
addStar: addStar,
removeStar: removeStar);
},
),
In your code, you have this line:
title: todoList[(todoList.length - 1) - index],
Let's imagine you have 3 items in the list list = [1,2,3]; The ListView.builder should iterate through them by saying
itemBuilder: (context, index) {
return Text(list[index].toString());
},
This way, the ListView.builder Widget will return a text with '1', '2' and '3' in this order since you call list[index] for the length of list (3 times) and each time index increments (starting from 0) so you call list[0], list[1] and list [2] (which return the three texts '1', '2' and '3'.) You, however, call list[(list.length-1) - index], so when index iterates from 0 to 2 you get:
list [ 3 - 1 - 0 ] = list [2] = text '3'
list [ 3 - 1 - 1 ] = list [1] = text '2'
list [ 3 - 1 - 2 ] = list [0] = text '1'
with this you can clearly see that you get a reverse list, hence, if you use list.reversed, you will get the correct list.
The solution is to use Keys with a ListView and not a ListView.builder as it doesn't seem to work with ListView.builder, the problem actually comes from reversing the list, since when the list is reversed every time a new task is added the objects in the Widget Tree won't be in line with their corresponding Object in the Element Tree.
ListView(children: todoList.reversed.map((title) => Todo(title: title, key: ValueKey(title))).toList())
Related
I want to persist value after user leaves page, also I would like to persist selected values, so I found out shared prefernces and I save it locally, but when I left page and return it remains unselected.
So I decided to convert my multipleSelected list to String, because sharedprefernces can't save list of ints and sfter that save selected values in lists. So how can i solve that problem when user leaves page and selected items become unselected.
class DataBaseUser extends StatefulWidget {
const DataBaseUser({Key? key}) : super(key: key);
#override
State<DataBaseUser> createState() => _DataBaseUserState();
}
class _DataBaseUserState extends State<DataBaseUser> {
int index = 1;
/// add selected items from list
List multipleSelected = [];
/// another list to form the new list above previous one
List chosenListsAbove = [];
List basesNames = [];
SharedPreferences? sharedPreferences;
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Typographys.primaryColor,
appBar: PreferredSize(
preferredSize: const Size(125, 125),
child: AppBarService(),
),
body: Column(
children: [
// chosenOne(),
Card(
color: Typographys.gradientCard2,
child: ExpansionTile(
iconColor: Colors.white,
maintainState: true,
title: Text(
'Bases',
style: TextStyle(
fontFamily: 'fonts/Montserrat',
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 35),
),
children: [
SizedBox(
height: 10,
),
getDataBaseList(),
SizedBox(
height: 22,
),
getUpdateBaseButtons(),
SizedBox(
height: 10,
),
],
),
),
],
),
);
}
Widget getDataBaseList() {
return FutureBuilder<List>(
future: BasesService().GetBases(),
builder: (context, snapshot) {
List? baseNames = snapshot.data;
print(baseNames);
return ListView.builder(
shrinkWrap: true,
itemCount: baseNames?.length ?? 0,
itemBuilder: (context, i) {
Future<void> _onCategorySelected(bool selected, id) async {
final pref = await SharedPreferences.getInstance();
if (selected == true) {
setState(() {
multipleSelected.add(id);
List<String> stringsList =
multipleSelected.map((i) => i.toString()).toList();
// store your string list in shared prefs
pref.setStringList("stringList", stringsList);
List<String> mList =
(pref.getStringList('stringList') ?? <String>[]);
print('HERE');
print(mList);
print('HERE 2');
});
} else {
setState(
() {
multipleSelected.remove(id);
},
);
}
}
return Column(
children: [
ListTile(
title: Padding(
padding: const EdgeInsets.only(left: 1.0),
child: Text(
baseNames?[i]['name'] ?? 'not loading',
style: TextStyle(
fontFamily: 'fonts/Montserrat',
fontSize: 24,
fontWeight: FontWeight.w900,
color: Colors.white),
),
),
leading: Checkbox(
activeColor: Colors.green,
checkColor: Colors.green,
side: BorderSide(width: 2, color: Colors.white),
value: multipleSelected.contains(
baseNames?[i]['id'],
),
onChanged: (bool? selected) {
_onCategorySelected(selected!, baseNames?[i]['id']);
},
)
//you can use checkboxlistTile too
),
],
);
},
);
},
);
}
Widget getUpdateBaseButtons() {
return Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
FutureBuilder<bool>(
future: BasesService().SelectBaseAsync(multipleSelected.cast()),
builder: (context, snapshot) {
return ElevatedButton(
onPressed: () {
if (snapshot.data == true) {
BasesService().SelectBaseAsync(multipleSelected.cast());
print(multipleSelected.cast());
print(multipleSelected);
successSnackBar();
} else {
notSuccessSnackBar();
}
},
child: Text(
'Send bases',
style: TextStyle(
fontFamily: 'fonts/Montserrat',
fontSize: 22,
fontWeight: FontWeight.w900,
color: Colors.white,
letterSpacing: 2),
),
style: ElevatedButton.styleFrom(
minimumSize: Size(200, 40),
primary: Colors.green,
onPrimary: Colors.white,
),
);
return Container();
})
],
),
);
}
If I understand you correclty, cant you just save items in WillPopScope like
return WillPopScope(
onWillPop: () async => SaveMyPreferences,
child: const Scaffold(
body: Container(
color: Colors.red,
size: 50.0,
),
),
);
I found a solution. If your data that you want to save comes from the API and is constantly updated (as it was in my case), then you do not need to use the shared preference package. This package will not help you. In my case, in order to save the checkboxes selected by the user and after reloading the page to show him which items in the list were selected (I use checkboxes), I write to a file on the device and then read the saved data from this file. So you are going to need path_provider package and dart:io and these two functions
to write from function where you choose items
_onCategorySelected(bool selected, id) async {
final Directory directory =
await getApplicationDocumentsDirectory();
if (selected == true) {
multipleSelected.add(id);
} else {
multipleSelected.remove(id);
}
final File file = File('${directory.path}/my_file.json');
file.writeAsStringSync('{"selected": $multipleSelected}');
setState(() {});
}
to read from file:
Future<String> read() async {
String text = '';
try {
final Directory directory =
await getApplicationDocumentsDirectory();
final File file = File('${directory.path}/my_file.json');
text = await file.readAsString();
print('HELLO');
multipleSelected = json.decode(text)["selected"];
} catch (e) {
print("Couldn't read file");
}
return text;
}
and before the listview.builder comes, you need to use read() function ro read the saved values from file.
It is not the greatest solution (maybe, the worst one), but if you haven't got enough time and you don't have any state management and you just need to solve issue right now, it can be really helpfull.
I'm working on a quiz app as a personal project and what I want to do is make it possible for the user to name a question set. (Kind of like a folder for questions on a particular subject). I am using Riverpod. (I've worked with the provider package a couple of times) for state management but it seems I've missed a step or two because when I type in the name, I don't see it on the page. I hope I can be pointed in the right direction. Thanks
Class forRiverpod model which shows a list of type QuestionSetConstructor for taking the title. There is also a method for accepting the question title to add to the list
class RiverpodModel extends ChangeNotifier {
final List<QuestionSetConstructor> _questionSetList = [];
UnmodifiableListView<QuestionSetConstructor> get questionSet {
return UnmodifiableListView(_questionSetList);
}
void addTitleT(String title) {
final addQuestionTitle = (QuestionSetConstructor(title: title));
_questionSetList.add(addQuestionTitle);
notifyListeners();
}
int get count{
return questionSet.length;
}
}
`
This is for the alert dialog that will take the question title.
In the elevated button, I stated that I want the contents of the
text field to be added to the list in the Riverpod model.
void setQuestionNameMethod(context) {
showDialog(
context: context,
barrierDismissible: true,
builder: (BuildContext context) {
return SetQuestionNameAlertDialog();
});
}
class SetQuestionNameAlertDialog extends ConsumerStatefulWidget {
#override
_SetQuestionNameAlertDialogState createState() =>
_SetQuestionNameAlertDialogState();
}
class _SetQuestionNameAlertDialogState
extends ConsumerState<SetQuestionNameAlertDialog> {
final TextEditingController questionNameController = TextEditingController();
final riverPodModelProvider2 =
ChangeNotifierProvider((ref) => RiverpodModel());
#override
Widget build(
BuildContext context,
) {
final questionNameRef = ref.watch(riverPodModelProvider2);
return AlertDialog(
title: Text("Name of Question Set",
style: TextStyle(
color: Colors.blue[400],
fontSize: 20,
fontWeight: FontWeight.w600)),
content: TextField(
controller: questionNameController,
),
actions: [
Center(
child: ElevatedButton(
onPressed: () {
setState(() {
questionNameRef.addTitleT(questionNameController.text) ;
});
print(questionNameRef.questionSet.first.title);
Navigator.pop(context);
},
child: const Text("Save"))),
],
);
}
}
`
This is the page where the question title is shown as a list. However, for some reason it is not showing.
class QuestionSetPage extends ConsumerStatefulWidget {
#override
_QuestionSetPageState createState() => _QuestionSetPageState();
}
class _QuestionSetPageState extends ConsumerState<QuestionSetPage> {
final riverPodModelProvider =
ChangeNotifierProvider((ref) => RiverpodModel());
#override
Widget build(BuildContext context) {
final questionSetRef = ref.watch(riverPodModelProvider);
return Scaffold(
appBar: AppBar(
elevation: 0,
backgroundColor: Colors.grey[50],
centerTitle: true,
actions: [
IconButton(
onPressed: () {
setState(() {
setQuestionNameMethod(context);
// modalSheetMethod(context);
});
},
icon: Icon(
Icons.add,
size: 25,
color: Colors.blue[400],
))
],
),
body: Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
"Question Set List",
style: TextStyle(
color: Colors.blue[400],
fontSize: 30,
fontWeight: FontWeight.w600),
),
),
Expanded(
child: ListView.builder(
itemCount: questionSetRef.count,
itemBuilder: (BuildContext context, int index) {
return Tiles(
title: questionSetRef.questionSet[index].title,
);
}),
)
],
),
);
}
}
class Tiles extends StatelessWidget {
String title;
Tiles({required this.title});
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.fromLTRB(15, 3, 15, 3),
child: Material(
elevation: 2,
child: GestureDetector(
onTap: () {
Navigator.push(context, MaterialPageRoute(builder: (context) {
return QuestionSetsQuestionPage();
}));
},
child: ListTile(
title: Text(
title,
style: TextStyle(
color: Colors.blue[400],
fontSize: 17,
fontWeight: FontWeight.w400),
),
tileColor: Colors.white,
// subtitle: Text(
// "${questionSets[index].numberOfQuestions} number of questions",
// ),
leading: const Icon(
Icons.add_box_outlined,
size: 30,
),
),
),
),
);
}
}
I am trying to create a page that lists a number of questions. For each question I will have different answers. At the minute, whenever I select an answer, the same option on the other questions are selected at the same time. How can I avoid this and make each question its own entity? Here is my code:
class QuestionScreen extends StatefulWidget {
#override
_QuestionScreenState createState() => _QuestionScreenState();
}
class _QuestionScreenState extends State<QuestionScreen> {
List<bool> _isChecked;
final Map<String, Map> questions = {"milk comes from what animal":
{"horse": false, "monkey": false, "frog": false, "cow": true},
"what colour is the sea?":
{"red": false, "green": false, "blue": true, "yellow": false}};
#override
void initState() {
super.initState();
_isChecked = List<bool>.filled(questions.values.length, false);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
child: Text("questions page"),
),
Expanded(
child: new ListView.builder(
itemCount: questions.keys.length,
itemBuilder: (BuildContext ctxt, int questionTitleIndex) {
return Padding(
padding: const EdgeInsets.all(24.0),
child: Container(
height: MediaQuery.of(context).size.height * 0.45,
decoration: BoxDecoration(
color: OurTheme().ourCanvasColor,
borderRadius: BorderRadius.circular(25),
),
child: Column(
children: [
Text(
questions.keys.toList()[questionTitleIndex],
style: TextStyle(
color: Colors.white,
fontSize: 24,
fontWeight: FontWeight.w800),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: ListView.builder(
shrinkWrap: true,
itemCount: questions.values
.toList()[questionTitleIndex]
.keys
.length,
itemBuilder:
(BuildContext ctxt, int questionAnswersIndex) {
return Container(
decoration: BoxDecoration(border: Border.all()),
child: CheckboxListTile(
title: Text(
"${questionAnswersIndex + 1}. ${questions.values.toList()[questionTitleIndex].keys.toList()[questionAnswersIndex]}",
style: TextStyle(
color: Colors.white, fontSize: 16),
),
value: _isChecked[questionAnswersIndex],
controlAffinity:
ListTileControlAffinity.platform,
onChanged: (bool value) {
setState(
() {
_isChecked[questionAnswersIndex] =
value;
},
);
},
activeColor: OurTheme().ourCanvasColor,
checkColor: Colors.white,
),
);
},
),
)
],
),
),
);
},
),
)
],
),
);
}
}
I see a few problems here.
First since your need to maintain the answer for each question in your _isChecked. It would make more sense to make it a Map<String, String> instead of a List<bool>.
Inside it, the key will be the question title and the value will be the selected option title.
So, inside your initState, you will initiate it liket this.
Map<String, String> _isChecked = {}; // Initializing with empty map
#override
void initState() {
super.initState();
widget.questions.keys.forEach((key) {
// For each question we first set the answer as "". means nothing selected.
_isChecked[key] = "";
// We then loop over the options of that question to see if any option was already true and set it as the initial answer.
for (MapEntry entry in widget.questions[key]!.entries) {
if (entry.value) _isChecked[key] = entry.key;
}
});
}
After this, you just have to change the places in your code where you were using the _isChecked variable.
Here is the link to the full working code. Just replace all your code with the code in the link.
Result.
recently I have followed a Youtube tutorial where it shows how to add product item to cart. But in the video, it didn't show how to delete an item from cart. Video link: https://www.youtube.com/watch?v=K8d3qqbP3qk
ProductModel.dart
class ProductModel{
String name;
int price;
String image;
ProductModel(String name, int price, String image){
this.name = name;
this.price = price;
this.image = image;
}
}
ProductScreen.dart
import 'package:flutter/material.dart';
import '../ProductModel.dart';
import 'package:ecommerce_int2/models/product.dart';
class ProductScreen2 extends StatelessWidget {
final ValueSetter<ProductModel> _valueSetter;
ProductScreen2(this._valueSetter);
List<ProductModel> products = [
ProductModel("Grey Jacket", 100, 'assets/jacket_1.png'),
ProductModel("Brown Pants", 60, 'assets/jeans_9.png'),
ProductModel("Grey Pants", 50, 'assets/jeans_6.png'),
ProductModel("Orange Pants", 70, 'assets/jeans_8.png'),
ProductModel("Long Jeans", 80, 'assets/jeans_2.png'),
ProductModel("Black and Blue Cap", 40, 'assets/cap_2.png'),
ProductModel("Black Cap", 30, 'assets/cap_6.png'),
ProductModel("Red Cap", 35, 'assets/cap_4.png'),
];
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: ListView.separated(
itemBuilder: (context, index){
return ListTile(
leading: Image.asset(
products[index].image,
width: 100,
height: 100,
fit: BoxFit.fitWidth,
),
title: Text(products[index].name),
trailing: Text("\RM${products[index].price}", style: TextStyle(color: Colors.redAccent, fontSize: 20, fontWeight: FontWeight.w500),),
onTap: (){
_valueSetter(products[index]);
},
);
},
separatorBuilder: (context, index){
return Divider();
},
itemCount: products.length
),
);
}
}
CheckoutScreen.dart
import 'package:flutter/material.dart';
import 'package:ecommerce_int2/screens/address/add_address_page.dart';
import 'package:device_apps/device_apps.dart';
class CheckoutScreen extends StatelessWidget {
final cart;
final sum;
CheckoutScreen(this.cart, this.sum);
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
ListView.separated(
itemBuilder: (context, index){
return ListTile(
title: Text(cart[index].name),
trailing: Text("\RM${cart[index].price}", style: TextStyle(color: Colors.redAccent, fontSize: 20, fontWeight: FontWeight.w500),),
onTap: (){
},
);
},
separatorBuilder: (context, index){
return Divider();
},
itemCount: cart.length,
shrinkWrap: true,
),
Divider(),
Text("Total : \RM$sum", style: TextStyle(fontSize: 20, fontWeight: FontWeight.w500), textAlign: TextAlign.right,),
SizedBox(
height: 20,
),
Text("Remarks", style: TextStyle(fontSize: 20, fontWeight: FontWeight.w500),),
TextFormField(
decoration: InputDecoration(
hintText: ('Example: Red Cap: Free Size, Grey Jacket: UK, M Size'),
),
maxLines: 5,
),
SizedBox(
height: 50,
),
RaisedButton(
color: Theme.of(context).accentColor,
child: Text('Buy Now',style: TextStyle(color: Colors.white, fontSize: 20)),
onPressed: () {
Navigator.of(context)
.push(MaterialPageRoute(builder: (_) => AddAddressPage()));
},
),
RaisedButton(
color: Theme.of(context).accentColor,
child: Text('Try Out',style: TextStyle(color: Colors.white, fontSize: 20)),
onPressed: () => DeviceApps.openApp('com.DefaultCompany.clothestryingfunction2'),
),
],
),
);
}
}
add_to_cart.dart
import 'package:ecommerce_int2/ProductModel.dart';
import 'package:ecommerce_int2/screens/CheckoutScreen.dart';
import 'package:ecommerce_int2/screens/ProductScreen.dart';
import 'package:flutter/material.dart';
class CartApp extends StatefulWidget {
#override
_CartAppState createState() => _CartAppState();
}
class _CartAppState extends State<CartApp> {
List<ProductModel> cart = [];
int sum = 0;
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: 2,
child: Scaffold(
appBar: AppBar(
title: Text("Add To Cart"),
bottom: TabBar(
tabs: <Widget>[
Tab(text: "Products",),
Tab(text: "Cart",),
],
),
),
body: TabBarView(
children: <Widget>[
ProductScreen2((selectedProduct){
setState(() {
cart.add(selectedProduct);//update
sum = 0;
cart.forEach((item){
sum = sum + item.price;
});
});
}),
CheckoutScreen(cart, sum),
],
),
),
);
}
}
The goal is to remove the selected item from cart and minus the selected item's price from the sum. Can anyone tell me how to do that?
First, you need to add a callback in the CheckoutScreen:
class CheckoutScreen extends StatelessWidget {
final cart;
final sum;
final ValueSetter<ProductModel> _valueDeleter;
CheckoutScreen(this.cart, this.sum, this._valueDeleter);
...
After that, add the callback function when using it in the TabBarView in CartApp:
CheckoutScreen(cart, sum, (deleteProduct) {
setState(() {
// Use this loop instead of cart.removeWhere() to delete 1 item at a time
for (var i = 0; i < cart.length; i++) {
if (cart[i].name == deleteProduct.name) {
cart.removeAt(i);
break;
}
}
sum = 0;
cart.forEach((item) {
sum = sum + item.price;
});
});
}),
Finally, add a button in the ListTile within CheckoutScreen to initiate the delete action (I'm using a Row in title here for simplicity):
ListTile(
...
title: Row(
children: [
IconButton(
icon: Icon(Icons.delete),
color: Colors.red,
onPressed: () => _valueDeleter(cart[index]),
),
Text(cart[index].name),
],
),
trailing: Text(
...
I have two dropdown menu's. One Represent the Category and Other Represent the Sub Category. The values should be retrieved from firesotore. Where the collection is created as a Nested Collection.
Categories & SubCategories collection are shown in the image
class SelectCategory extends StatefulWidget {
#override
_SelectCategoryState createState() => _SelectCategoryState();
}
class _SelectCategoryState extends State<SelectCategory> {
AdminDatabaseMethods adminDatabaseMethods = AdminDatabaseMethods();
var selectedCategory;
#override
Widget build(BuildContext context) {
return StreamBuilder(
stream: adminDatabaseMethods.getCategories(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
}
List<DropdownMenuItem> categoryMenu = [];
for (var i = 0; i < snapshot.data.docs.length; i++) {
DocumentSnapshot snap = snapshot.data.docs[i];
categoryMenu.add(DropdownMenuItem(
child: Text(snap.data()["Category_Name"]),
value: "${snap.id}",
));
//adminDatabaseMethods.getSubCategories(snap);
}
return DropdownButton(
value: selectedCategory,
icon: Icon(
Icons.arrow_downward_sharp,
color: Colors.amber,
),
iconSize: 20,
elevation: 16,
hint: Text(
"Select Main Categories",
style: TextStyle(color: Colors.amber),
),
style: TextStyle(color: Colors.amber),
underline: Container(
height: 2,
color: Colors.amber[300],
),
onChanged: (categoryValue) {
setState(() {
selectedCategory = categoryValue;
adminDatabaseMethods.getSubCategories(selectedCategory);
});
},
items: categoryMenu,
);
});
}
}
This is how I have created the list of Categoires and followed the same way to create the subcategories. What I require is I when i press the category I need to pass the documentsnapshot id to the other class.
Where,
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
SizedBox(
width: 15,
),
SelectCategory(),
SizedBox(
width: 15,
),
SelectsubCategories(),
SizedBox(
width: 15,
),
],
),
Categories and Sub Categories are given like this. Is there a way of passing the data. As i am new to flutter please suggest me a method thanks.
I think what you're looking for is a state management solution. Learn more here.