How can I make an Expandable Panel editable? - flutter

I'm new to flutter and I'm currently working on an app for my school project. At the moment I cannot make my expanded panel editable, could anybody tell me how I can make something editable or if it's not possible tell me an alternativ solution? Here's the code btw.:
class MyItem {
MyItem({this.isExpanded = false, required this.header, required this.body});
bool isExpanded;
final String header;
final String body;
}
class fourthPage extends StatefulWidget {
#override
list createState() => list();
}
class list extends State<fourthPage> {
final List<MyItem> _items = <MyItem>[
MyItem(
header: "header1",
body: "text1"),
MyItem(
header: "header2",
body: "text2"),
MyItem(
header: "header3",
body: "text3"),
];
#override
Widget build(BuildContext context) {
return _buildPage();
}
Widget _buildPage() {
return SafeArea(
top: true,
child: Scaffold(
appBar: AppBar(
title: const Text("page4"),
),
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const Text(
'Assignments:',
style: TextStyle(fontSize: 35),
),
ExpansionPanelList(
expansionCallback: (int index, bool isExpanded) {
setState(() {
_items[index].isExpanded = !_items[index].isExpanded;
});
},
children: _items.map((MyItem item) {
return ExpansionPanel(
headerBuilder: (BuildContext context, bool isExpanded) {
return Text(item.header);
},
isExpanded: item.isExpanded,
body: Container(child: Text(item.body)));
}).toList(),
),
ElevatedButton(
child: const Text('Page 1'),
onPressed: () {
Navigator.of(this.context).push(MaterialPageRoute(
builder: (context) => const FirstPage()));
},
),
ElevatedButton(
child: const Text('Page 2'),
onPressed: () {
Navigator.of(this.context).push(MaterialPageRoute(
builder: (context) => const SecondPage()));
},
),
ElevatedButton(
child: const Text('Page 3'),
onPressed: () {
Navigator.of(this.context).push(MaterialPageRoute(
builder: (context) => const ThirdPage()));
},
),
],
),
),
));
}
}
I have thought about using onclick in combination with a function but I just can't do it If anybody could help me I would really appreciate it

Related

Pass items to a list to previous screen in Flutter

I have a search page that displays names with an add icon. When I press the add icon I want to pass the name to my previous screen that displays a list with names. I tried to do it as you can see down in my code but I have an error that my Athlete model doesn't have the constructor add. Can you help me figure out how to display the names in my list in previous screen? Thanks in advance!
My first screen that I display a list with names:
class AthleteScreen extends StatefulWidget {
const AthleteScreen({Key? key}) : super(key: key);
#override
State<AthleteScreen> createState() => _AthleteScreenState();
}
class _AthleteScreenState extends State<AthleteScreen> {
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
Future<List<Athlete>>? futureAthletebyTeamKey;
final List<Athlete> _athlete = [];
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
title: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
const Text(
'Athletes'),
actions: <Widget>[
Row(
children: [
IconButton(
onPressed: () {
Navigator.of(context)
.push<Athlete>(
MaterialPageRoute(builder: (_) => const AddAthlete()))
.then((value) => setState(() {
if (value != null && value is Athlete) {
Athlete.add(_athlete[index].lastName, _athlete[index].firstName,_athlete[index].fatherName); //here is when I push to the page where the names that I want to add are displayed
}
}));
},
icon: const Icon(Icons.add),
color: Colors.black,
iconSize: 30.0,
),
],
),
],
),
body: Stack(
children: [
SingleChildScrollView(
child: Column(children: [
FutureBuilder<List<Athlete>>(
future: futureAthletebyTeamKey,
builder: (context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
List<Athlete> _athlete = snapshot.data;
return ListView.builder(
itemCount: _athlete.length,
itemBuilder: (BuildContext context, int i) {
return CheckboxListTile(
title: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Flexible(
child: Text(
'${_athlete[i].lastName} ${_athlete[i].firstName}',
),
),
],
),
} else if (snapshot.hasError) {
logger.e('${snapshot.error}');
}
return const Center(
heightFactor: 20,
child: CircularProgressIndicator.adaptive(),
);
},
),
]),
),
);
}
}
My second screen where the names that I want to add in the list of my first page are displayed
class AddAthlete extends StatefulWidget {
const AddAthlete({Key? key}) : super(key: key);
#override
State<AddAthlete> createState() => _AddAthleteState();
}
class _AddAthleteState extends State<AddAthlete> {
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
Future<List<Athlete>>? futureSearchAthleteByName;
#override
void initState() {
futureSearchAthleteByName =
ApiService.searchAthletesByName(context) as Future<List<Athlete>>?;
text = myController.text;
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: const <Widget>[
Text(
'Add Athletes',
),
],
),
),
body: SingleChildScrollView(
child: Column(
children: [
Stack(
children: [
SingleChildScrollView(
child: Column(children: [
FutureBuilder<List<Athlete>>(
future: futureSearchAthleteByName,
builder: (context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
List<Athlete> _athlete = snapshot.data;
return ListView.builder(
itemCount: _athlete.length,
itemBuilder: (BuildContext context, int index) {
if (myController.text == '') {
return Container();
} else if (myController.text != '' &&
_athlete[index]
.lastName!
.toLowerCase()
.contains(myController.text
.toLowerCase()) ||
_athlete[index]
.firstName!
.toLowerCase()
.contains(
myController.text.toLowerCase())) {
return Column(
children: [
ListTile(
title: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment:
CrossAxisAlignment.start,
),
Row(
children: [
Flexible(
child: Text(
'${_athlete[index].lastName} ${_athlete[index].firstName}',
),
),
],
),
Row(
children: [
Flexible(
child: Text(
'(${_athlete[index].fatherName})',
),
),
],
),
],
),
trailing: IconButton(
icon: const Icon(
Icons.add,
color: Colors.black,
),
onPressed: () {
Navigator.pop(
context,
Athlete(
lastName: _athlete[index]
.lastName,
firstName: _athlete[index]
.firstName,
fatherName: _athlete[index]
.fatherName));
print(_athlete[index].lastName);
print(_athlete[index].firstName);
print(_athlete[index].fatherName); \\here is when I pop the names in my previous screen
},
),
),
],
);
}
});
} else if (snapshot.hasError) {
logger.e('${snapshot.error}');
}
return Container();
},
),
]),
),
],
),
],
),
),
);
}
}
If I was you I might do it in a different way
I add all the user id to the list on the second screen and pass the list to the second screen
in the first screen I call the API and get all the data by id and show it
(when a user doesn't select any element don't call the API)

FilterChip inside ModalBottomSheet

Hi I'm a beginner flutter developer, I have a StatefulWidget widget and a ListView here is a button to display ModalBottomSheet
The ModalBottomSheet has a FilterChip widget that allows the user to apply some filters to the ListView, but I would like to keep the FilterChip state even after the user pop the ModalBottomSheet.
class AvailableMeals extends StatefulWidget {
static const routeName = 'available-meals';
#override
_AvailableMealsState createState() => _DietAvailableMealsState();
}
class _DietAvailableMealsState extends State<DietAvailableMeals> {
bool status = false;
#override
Widget build(BuildContext context) {
buildFilterBox() {
return Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
child: Text(
'SelectFilter',
style: TextStyle(fontSize: 10.sp),
),
),
Container(
child: Wrap(
spacing: 25,
children: [
FilterChip(
selected: status,
label: Text('Vegan'),
onSelected: (value) {
setState(() {
status = value;
});
})
],
),
),
],
),
);
}
return Scaffold(
appBar: AppBar(
title: Text('Meals'),
actions: [
IconButton(
onPressed: () {
showModalBottomSheet(
context: context,
builder: (context) {
return buildFilterBox();
});
},
icon: Icon(Icons.search))
],
),
body: Container(child : Column(children: [
Expanded(
child: ListView.builder(
itemBuilder: (ctx, index) => ChangeNotifierProvider.value(
value: _customList[index], child: MealCard(_customList[index])),
itemCount: _customList.length,
));
] ))
}

error: The argument type 'Null' can't be assigned to the parameter type 'Map<String, dynamic>'

I am writing my first Flutter App with some online tutorials and I found error that I can't fix it.
I am trying to add Navigation by Navigator, but I can't understand why it doesn't work.
Once I am using Navigator in GestureDetector and it works fine, but I don't know what I supposed to do in floatingActionButton to make it work the same way. Note(NoteMode.Adding, null) probably should be something else instead null, because this null is making error (error from title). Can someone explain me what I am doing wrong and what I don't undarstand
Note List
#override
_NoteListState createState(){return _NoteListState();}
}
class _NoteListState extends State<NoteList> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Notes"),
),
body: FutureBuilder(
future: NoteProvider.getNoteList(),
builder: (context, AsyncSnapshot snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
final notes = snapshot.data;
return ListView.builder(
itemBuilder: (context, index) {
return GestureDetector(
onTap: () {
Navigator.push(
context, MaterialPageRoute(builder: (context) =>
Note(NoteMode.Editing, (notes as dynamic)[index]))
);
},
child: Card(
child: Padding(
padding: const EdgeInsets.only(
top: 30.0, bottom: 30.0, left: 13, right: 22),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
_NoteTitle((notes as dynamic)[index]['title']),
Container(height: 3,),
_NoteText((notes as dynamic)[index]['text']),
],
),
),
),
);
},
itemCount: notes.length,
);
}
return Center(child: CircularProgressIndicator());
},
),
floatingActionButton: FloatingActionButton(
onPressed: () {
Navigator.push(context, MaterialPageRoute(builder: (context) => Note(NoteMode.Adding, null)));
},
child: Icon(Icons.add),
),
);
}
}
Note
enum NoteMode{
Editing,
Adding
}
class Note extends StatefulWidget{
final NoteMode noteMode;
final Map<String, dynamic> note;
Note(this.noteMode, this.note,);
#override
State<Note> createState() => _NoteState();
}
class _NoteState extends State<Note> {
final TextEditingController _titleController = TextEditingController();
final TextEditingController _textController = TextEditingController();
List<Map<String, String>> get _notes => NoteInheritedWidget.of(context).notes;
#override
void didChangeDependencies(){
if(widget.noteMode == NoteMode.Editing){
_titleController.text = widget.note['title'];
_textController.text = widget.note['text'];
}
super.didChangeDependencies();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
widget.noteMode == NoteMode.Adding ? 'Add note' : 'Edit note',
),
),
body: Padding(
padding: const EdgeInsets.all(40.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextField(
controller: _titleController,
decoration: InputDecoration(
hintText: "Note title",
),
),
Container(height: 8,),
TextField(
controller: _textController,
decoration: InputDecoration(
hintText: "Note text",
),
),
Container(height: 15,),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
_NoteButton('SAVE', Colors.lightBlue, (){
final title = _titleController.text;
final text = _textController.text;
if(widget.noteMode == NoteMode.Adding){
NoteProvider.insertNote({
'title': title,
'text': text
});
} else if (widget.noteMode == NoteMode.Editing){
NoteProvider.updateNote( {
'id': widget.note['id'],
'title': _titleController.text,
'text': _textController.text,
});
}
Navigator.pop(context);}),
_NoteButton('DISCARD', Colors.grey, (){Navigator.pop(context);}),
widget.noteMode == NoteMode.Editing ?
_NoteButton('DELETE', Colors.redAccent, () async {
await NoteProvider.deleteNote(widget.note['id']);
Navigator.pop(context);})
: Container(),
],
)
],
),
),
);
}
}
Either you have to pass Map in place of null because you are receiving a Map on that page
Navigator.push(context, MaterialPageRoute(builder: (context) => Note(NoteMode.Adding, {"key":"value"})));
or you have to make Map nullable as
class Note extends StatefulWidget{
final NoteMode noteMode;
final Map<String, dynamic>? note;
Note(this.noteMode, this.note,);
#override
State<Note> createState() => _NoteState();
}

Flutter Error : Could not find the correct Provider<Cart> above this ProductLandingPage Widget

I am creating an e-commerce app where homepage is kind of page where all fields like categories and other info is given.
here is my flow of screens ...
HomeScreen -> CategoryPage -> ProductByCategory -> ProductLandingPage
I am getting error. New to Coding and learning Providers for 1st time, Not able to resolve this issue.
Error: Could not find the correct Provider above this ProductLandingPage Widget
To fix, please:
Ensure the Provider is an ancestor to this ProductLandingPage Widget
Provide types to Provider
Provide types to Consumer
void main() {
runApp(MaterialApp(
home: MultiProvider(
providers: [
ChangeNotifierProvider.value(
value: Cart(),
)
],
child: HomeScreen(),
),
debugShowCheckedModeBanner: false,
));
}
class HomeScreen extends StatefulWidget {
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.redAccent,
title: Text('Factory2Homes'),
actions: <Widget>[
IconButton(icon: Icon(Icons.search), onPressed: () {}),
Consumer<Cart>(
builder: (_, cart, ch) => Badge(
child: ch,
value: cart.itemCount.toString(),
),
child: IconButton(
icon: Icon(
Icons.shopping_cart,
),
onPressed: () {
},
),
),
],
),
body: SingleChildScrollView(
child: Column(
children: <Widget>[
Container(height: 500, child: CategoryPage()),
],
),
),
);
}
}
class CategoryPage extends StatefulWidget {
#override
_CategoryPageState createState() => _CategoryPageState();
}
class _CategoryPageState extends State<CategoryPage> {
#override
Widget build(BuildContext context) {
return FutureBuilder<List<AllCategory>>(
future: getCategoryList(http.Client()),
builder: (context, snapshot) {
if (snapshot.hasError) print(snapshot.error);
return snapshot.hasData
? ListOfCategories(
categories: snapshot.data,
)
: Center(
child: CircularProgressIndicator(
backgroundColor: Colors.red,
));
},
);
}
}
class ListOfCategories extends StatelessWidget {
final List<AllCategory> categories;
ListOfCategories({this.categories});
#override
Widget build(BuildContext context) {
return GridView.builder(
physics: NeverScrollableScrollPhysics(),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
),
itemCount: categories.length,
itemBuilder: (context, index) {
return InkWell(
onTap: (){
Navigator.push(context, MaterialPageRoute(builder: (context) => ProductByCategory(category: categories[index],)));
},
child: Image.network(categories[index].categoryIcon));
},
);
}
}
class ProductByCategory extends StatefulWidget {
final AllCategory category;
final CarouselSlider carouselslider;
ProductByCategory({this.category, this.carouselslider});
#override
_ProductByCategoryState createState() => _ProductByCategoryState();
}
class _ProductByCategoryState extends State<ProductByCategory> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: <Widget>[
Consumer<Cart>(
builder: (_, cart, ch) => Badge(
child: ch,
value: cart.itemCount.toString(),
),
child: IconButton(
icon: Icon(
Icons.shopping_cart,
),
onPressed: () {
},
),
),
IconButton(icon: Icon(Icons.search), onPressed: () {}),
],
),
body: FutureBuilder<List<Product>>(
future: getCategoryByProduct(http.Client(), widget.category.id),
builder: (context, snapshot) {
if (snapshot.hasError) print(snapshot.error);
if (snapshot.hasData) {
return ProductByCategoryScreen(
product: snapshot.data,
);
} else {
return Center(child: CircularProgressIndicator());
}
},
),
);
}
}
class ProductByCategoryScreen extends StatefulWidget {
final List<Product> product;
ProductByCategoryScreen({this.product});
#override
_ProductByCategoryScreenState createState() =>
_ProductByCategoryScreenState();
}
class _ProductByCategoryScreenState extends State<ProductByCategoryScreen> {
#override
Widget build(BuildContext context) {
return GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
),
itemCount: widget.product.length,
itemBuilder: (context, index) {
return InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
ProductLandingPage(widget.product[index])));
},
child:
Card(child: Image.network(widget.product[index].productPhoto)));
},
);
}
}
class ProductLandingPage extends StatefulWidget {
final Product product;
ProductLandingPage(this.product);
#override
_ProductLandingPageState createState() => _ProductLandingPageState();
}
class _ProductLandingPageState extends State<ProductLandingPage> {
#override
Widget build(BuildContext context) {
final cart = Provider.of<Cart>(context, listen: false);
return Scaffold(
appBar: AppBar(
actions: <Widget>[
],
),
body: SingleChildScrollView(
child: Column(
children: <Widget>[
Container(
color: Colors.green,
height: MediaQuery.of(context).size.height / 2,
child: Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Image.network(widget.product.productPhoto),
),
),
Divider(
thickness: 1,
),
Container(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(this.widget.product.productName),
),
),
Divider(),
Container(
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.only(left: 10.0),
child: Text(
'₹' + '${this.widget.product.productSalePrice}',
style: TextStyle(
fontSize: 30, fontWeight: FontWeight.w500),
),
),
],
),
Padding(
padding: const EdgeInsets.only(left: 8.0),
child: Center(
child: Text(
'MRP:' + '${this.widget.product.productListPrice}'),
),
),
],
),
),
Divider(),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(left: 10.0),
child: Text(
'Description',
style:
TextStyle(fontSize: 18, fontWeight: FontWeight.w500),
),
),
],
),
Container(
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Text(this.widget.product.productDescription),
),
),
],
),
),
bottomNavigationBar: Container(
width: MediaQuery.of(context).size.width,
height: 45.0,
child: RaisedButton(
onPressed: () {
cart.addItem(
'${widget.product.productId}',
widget.product.productListPrice,
widget.product.productName,
);
},
color: Colors.redAccent,
child: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(
Icons.card_travel,
color: Colors.white,
),
SizedBox(
width: 4.0,
),
Text(
"ADD TO CART",
style: TextStyle(color: Colors.white),
),
],
),
),
),
));
}
}
class CartItem {
final String id;
final String title;
final int quantity;
final int price;
CartItem({
this.id,
this.title,
this.quantity,
this.price,
});
}
class Cart with ChangeNotifier {
Map<String, CartItem> _items;
Map<String, CartItem> get items {
return {..._items};
}
int get itemCount{
return _items==null ?0 :_items.length;
}
void addItem(
String productId,
int productListPrice,
String productName,
) {
if (_items.containsKey(productId)) {
_items.update(
productId,
(existingCartItem) => CartItem(
id: existingCartItem.id,
title: existingCartItem.title,
price: existingCartItem.price,
quantity: existingCartItem.quantity + 1,
));
} else {
_items.putIfAbsent(
productId,
() => CartItem(
id: DateTime.now().toString(),
title: productName,
price: productListPrice,
quantity: 1,
));
}
}
}
The idea of Provider is to lift the state management above the widgets so different children can easily access its state. So it would be helpful if you moved the HTTP request from the widget tree (where it will be called every time the UI updates, so users use more bandwidth than needed) to a provider that is created above the tree. Therefore the state doesn't need to passed around from widget to widget.
Try watching this amazing talk from the flutter team to get a better understanding of how to use provider: https://youtu.be/d_m5csmrf7I
Bdw read this StackOverflow answer about why .value isn't what you desire: How to deal with unwanted widget build?
So you should make the app like this
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => Cart(),
child: MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Factory2Homes',
home: HomeScreen(),
),
);
}
}
i got it working by changing main.dart code to below code:
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider.value(value: Cart(),
child:MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Factory2Homes',
home: HomeScreen(),
),);
}
}

Flutter: Passing Data between Classes Resets the Other Value

I try to pass sRegions value from Regions class to the AddAd class
and passing sCategory value from Categories class to AddAd class
but when I click and pass this value , it resets the other value to initial value
I want to the values inside AddAd class to be separated and do not affect each other
Here is AddAd() class:
class AddAd extends StatefulWidget {
final String sRegion;
final String sCategory;
AddAd(this.sRegion,this.sCategory, {Key key}):super(key: key);
#override
_AddAdState createState() => _AddAdState();
}
ListTile(
title: Text("Location"),
subtitle: widget.sRegion == null ? Text("Where exactly") : Text(widget.sRegion),
),
ListTile(
title: Text("Category"),
subtitle: widget.sCategory == null ? Text("Department") : Text(widget.sCategory),
),
and here is how I pass data from Regions class:
child: Text(item),
onPressed: () {
sRegion = item;
Navigator.push(context, new MaterialPageRoute(builder: (context) => AddAd(sRegion, sCategory)));
print(sRegion);
},
and here how I pass data from Categories class:
class _RegionsState extends State<Regions> {
String sRegion;
String sCategory;
#override
Widget build(BuildContext context) {
return Directionality(
textDirection: TextDirection.rtl,
child: Scaffold(
appBar: AppBar(
title: Text("Location"),
actions: <Widget>[
FlatButton(
child: Text('Cancel', style: TextStyle(color: Colors.white),),
onPressed: (){
Navigator.pushReplacement(context, MaterialPageRoute(builder: (context)=>AddAd(sRegion, sCategory)));
},
)
],
),
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.only(top:15.0),
child: Container(
child: Column(
children: <Widget>[
for(var item in regions) FlatButton(
child: Text(item), onPressed: (){
sRegion = item;
Navigator.push(context, new MaterialPageRoute(builder: (context) => AddAd(sRegion, sCategory)));
print(sRegion);
},),
Divider(),
],
),
),
),
),
),
);
}
}