How to add icon/image along with popup menu in flutter? - flutter

Below is my code.
var choices = ['Settings', 'Log Out'];
void choiceAction(String choice){
print(choice);
}
Widget aapBarSection(String title, Color color, BuildContext context){
return AppBar(
title: Text(title, style:TextStyle(fontFamily: 'Poppins-Regular'), ),
centerTitle: true,
backgroundColor: color,
actions: [
PopupMenuButton<String>(
onSelected: choiceAction,
itemBuilder: (BuildContext context){
return choices.map((String choice) {
return PopupMenuItem<String>(
value: choice,
child: Text(choice),
);
}).toList();
},
)
],
leading: IconButton(
icon: Icon(Icons.arrow_back),
onPressed: (){
exit(0);
},
),
);
}
menu items are showing but how to show image/icon as per the items like for settings(settings icon) and for logout(logout icon) with it?
Can anybody help me please!

You can use ListTile as child or Row.
PopupMenuItem<String>(
value: choice,
child: ListTile(
leading: Icon(Icons.work), // your icon
title: Text(choice),
),
)

var choices = [
{'title': 'Home', 'icon': const Icon(Icons.home)},
{'title': 'Profile', 'icon': const Icon(Icons.people)},
{'title': 'Logout', 'icon': const Icon(Icons.logout)}
];
var actions = [
PopupMenuButton<String>(
onSelected: choiceAction,
itemBuilder: (BuildContext ctx) {
return choices.map((ch) {
return PopupMenuItem<String>(
value: ch['title'].toString(),
child: ListTile(
leading: ch['icon'] as Widget,
title: Text(ch['title'].toString())));
}).toList();
})
];
void choiceAction(String choice){
print(choice);
}
class _HomeVendorPageState extends State<HomeVendorPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
actions: actions,
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const [])));
}
}

Related

how get selected index of multiple ExpansionTile in flutter

how get selected index of multiple ExpansionTile in flutter ?
i need sidebar menu with multiple expansiontile and listtile.
how can i get selected index to change selected color menu with provider or bloc ?
children: [
ExpansionTile(
title: Text('main a'),
children: [
ListTile(
title: Text('a1'),
),
ListTile(
title: Text('a2'),
),
ExpansionTile(
title: Text('a3'),
children: [
ListTile(
title: Text('a31'),
),
ListTile(
title: Text('a32'),
),
ListTile(
title: Text('a32'),
),
],
),
],
),
ExpansionTile(
title: Text('main b'),
children: [
ListTile(
title: Text('b1'),
),
ListTile(
title: Text('b2'),
),
ListTile(
title: Text('b3'),
),
],
),
],
You can use onTap from ListTile, and create state variables to hold selected item. Like here I am using String. Based on your data, creating model class or map might be better choice.
String? aValue;
....
ExpansionTile(
title: Text('main a'),
children: [
ListTile(
title: Text('a1'),
onTap: () {
aValue = "a1";
setState(() {});
},
),
You can use a ListView to contain the ExpansionTile widgets and a ListTile widgets. Then you can use a currentIndex variable to keep track of the index of the currently selected menu item. You can use a Provider or BLoC to manage the currentIndex variable and to notify the widget tree when the value of currentIndex changes.
Here is the full code
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
runApp(
ChangeNotifierProvider(
create: (context) => MenuModel(),
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: HomePage(),
);
}
}
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('ExpansionTile Demo'),
),
body: MenuList(),
);
}
}
class MenuList extends StatelessWidget {
#override
Widget build(BuildContext context) {
final model = Provider.of<MenuModel>(context);
return ListView(
children: <Widget>[
ExpansionTile(
title: Text('Menu 1'),
children: <Widget>[
ListTile(
title: Text('Menu 1.1'),
onTap: () {
model.updateIndex(0);
},
selected: model.currentIndex == 0,
),
ListTile(
title: Text('Menu 1.2'),
onTap: () {
model.updateIndex(1);
},
selected: model.currentIndex == 1,
),
],
),
ExpansionTile(
title: Text('Menu 2'),
children: <Widget>[
ListTile(
title: Text('Menu 2.1'),
onTap: () {
model.updateIndex(2);
},
selected: model.currentIndex == 2,
),
ListTile(
title: Text('Menu 2.2'),
onTap: () {
model.updateIndex(3);
},
selected: model.currentIndex == 3,
),
],
),
],
);
}
}
class MenuModel with ChangeNotifier {
int _currentIndex = 0;
int get currentIndex => _currentIndex;
void updateIndex(int index) {
_currentIndex = index;
notifyListeners();
}
}
This is a hassle to dynamically change the color of the ListTile() which have two different parent widget but with some extra code, you can do the same.
Full Code
// You can also use `Map` but for the sake of simplicity I'm using two separate `List`.
final List<String> _parentlist1 = ["a1", "a2"];
final List<String> _childOfParentlist1 = ["a31", "a32", "a34"];
final List<bool> _isSelectedForParentList1 = List.generate(
2,
(i) =>
false); // Fill it with false initially and this list for all the textList
final List<bool> _isSelectedForChildOfParentList1 =
List.generate(2, (i) => false);
#override
Widget build(BuildContext context) {
return Scaffold(
body: ExpansionTile(
title: const Text('main a'),
children: [
ListView.builder(
itemBuilder: (_, i) {
return ListTile(
tileColor: _isSelectedForParentList1[i]
? Colors.blue
: null, // If current item is selected show blue color
title: Text(_parentlist1[i]),
onTap: () => setState(() => _isSelectedForParentList1[i] =
!_isSelectedForParentList1[i]), // Reverse bool value
);
},
),
ExpansionTile(
title: const Text('a3'),
children: [
ListView.builder(
itemBuilder: (_, i) {
return ListTile(
tileColor: _isSelectedForChildOfParentList1[i]
? Colors.blue
: null, // If current item is selected show blue color
title: Text(_childOfParentlist1[i]),
onTap: () => setState(() =>
_isSelectedForChildOfParentList1[i] =
!_isSelectedForChildOfParentList1[
i]), // Reverse bool value
);
},
),
],
),
],
),
);
}

How to navigate to other screen from navigation drawer in flutter

i have tried in the following way but it is not working please tell me the solutions
import 'package:book_recommendation_app/about.dart';
import 'package:book_recommendation_app/home.dart';
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
void onTap(menuItem) {
switch (menuItem) {
case 'item1':
print('item1 clicked');
break;
case 'item2':
print('item2 clicked');
break;
case 'item3':
print('item3 clicked');
break;
}
}
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
var menuItems = <String>['item1', 'item2', 'item3'];
return MaterialApp(
title: 'Book Reccomendation Demo',
home: Scaffold(
appBar: AppBar(
title: Text('Home'),
actions: <Widget>[
PopupMenuButton<String>(
onSelected: onTap,
itemBuilder: (BuildContext context) {
return menuItems.map((String choice) {
return PopupMenuItem<String>(
child: Text(choice),
value: choice,
);
}).toList();
})
],
),
body: searchBar(),
drawer: Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: [
const DrawerHeader(
decoration: BoxDecoration(
color: Colors.blue,
),
child: Text('Drawer Header'),
),
ListTile(
title: const Text('Item 1'),
onTap: () {
Navigator.pop(context);
},
),
ListTile(
title: const Text('About us'),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => AboutUs()),
);
Navigator.pop(context);
},
),
],
),
),
),
// home: const MyHomePage(title: 'Book Reccomendation Demo Home Page'),
);
}
}
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
var menuItems = <String>['item1', 'item2', 'item3'];
return MaterialApp(
title: 'Book Reccomendation Demo',
home: Scaffold(
appBar: AppBar(
title: Text('Home'),
actions: <Widget>[
PopupMenuButton<String>(
onSelected: onTap,
itemBuilder: (BuildContext context) {
return menuItems.map((String choice) {
return PopupMenuItem<String>(
child: Text(choice),
value: choice,
);
}).toList();
})
],
),
body: searchBar(),
drawer: Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: [
const DrawerHeader(
decoration: BoxDecoration(
color: Colors.blue,
),
child: Text('Drawer Header'),
),
ListTile(
title: const Text('Item 1'),
onTap: () {
Navigator.pop(context);
},
),
ListTile(
title: const Text('About us'),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => AboutUs()),
);
Navigator.pop(context);
},
),
],
),
),
),
// home: const MyHomePage(title: 'Book Reccomendation Demo Home Page'),
);
}
}
Do not call Navigator.pop(context) immediately afther calling Navigator.push. Because for me it looks like your currently pushing to the next screen, but immediately popping again so that it will never be reached.
In this ListTile
ListTile(
title: const Text('About us'),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => AboutUs()),
);
Navigator.pop(context);
},
),
You use Navigator.Push(context,MaterialPageRout(builder: (context) => AboutUs()));
then you use Navegator.pop(context);
that means you push a new screen then you closed it and here is the problem
you need just to remove this line Navigator.pop(context);
You can use GetX too for navigate your pages in easier way if you don't want to go with MaterialPageRoute.
ListTile(
title: const Text('About us'),
onTap: () {
Get.to(AboutUs());
Navigator.pop(context);
},
),
Pop the drawer before pushing to new screen,
Like
Navigator.pop(context);
Navigator.push(context,
MaterialPageRoute(builder: (context) => AboutUs(),
),
);

How can I make an Expandable Panel editable?

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

Understanding ListView.builder

Okay, so I think I am stuck with flutter builder a little bit.
I've created simple app, just to make my question easier:
I have a data class:
class DataLists {
List<ListTile> lists = [
ListTile(
leading: Text('Tile Leading 1'),
title: Text('Tile Title 1'),
subtitle: Text('Tile Subtitle 1'),
trailing: Text('Tile Trailing 1'),
),
ListTile(
leading: Text('Tile Leading 2'),
title: Text('Tile Title 2'),
subtitle: Text('Tile Subtitle 2'),
trailing: Text('Tile Trailing 2'),
),
ListTile(
leading: Text('Tile Leading 3'),
title: Text('Tile Title 3'),
subtitle: Text('Tile Subtitle 3'),
trailing: Text('Tile Trailing 3'),
),
ListTile(
leading: Text('Tile Leading 4'),
title: Text('Tile Title 4'),
subtitle: Text('Tile Subtitle 4'),
trailing: Text('Tile Trailing 4'),
),
ListTile(
leading: Text('Tile Leading 5'),
title: Text('Tile Title 5'),
subtitle: Text('Tile Subtitle 5'),
trailing: Text('Tile Trailing 5'),
),
];
}
And main dart file:
import 'package:flutter/material.dart';
import 'package:learning/data.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: TestTile(),
);
}
}
class TestTile extends StatefulWidget {
#override
_TestTileState createState() => _TestTileState();
}
class _TestTileState extends State<TestTile> {
DataLists dataLists = DataLists();
TextEditingController leadingController = TextEditingController();
TextEditingController titleController = TextEditingController();
TextEditingController subtitleController = TextEditingController();
TextEditingController trailingController = TextEditingController();
Future<String> createDialog(BuildContext context) {
return showDialog(context: context, builder: (context) {
return SimpleDialog(
title: Text('Input data: '),
children: [
TextField(
controller: leadingController,
),
TextField(
controller: titleController,
),
TextField(
controller: subtitleController,
),
TextField(
controller: trailingController,
),
MaterialButton(
child: Text('Submit'),
onPressed: () {
Navigator.of(context).pop(leadingController.text);
setState(() {
List<ListTile> tempList = dataLists.lists;
if (titleController.text.isNotEmpty && leadingController.text.isNotEmpty && subtitleController.text.isNotEmpty && trailingController.text.isNotEmpty) {
tempList.add(
ListTile(
leading: Text(leadingController.text),
title: Text(titleController.text),
subtitle: Text(subtitleController.text),
trailing: Text(trailingController.text),
),
);
dataLists.lists = tempList;
} else {
print('Null values');
}
leadingController.clear();
titleController.clear();
subtitleController.clear();
trailingController.clear();
});
},
),
],
);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Test Tile'),
),
body: Container(
child: SafeArea(
child: ListView(
children: <ListTile>[
for (ListTile e in dataLists.lists)
e
],
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
createDialog(context);
setState(() {
});
},
child: Icon(Icons.add),
backgroundColor: Colors.blue,
),
);
}
}
The problem is: I cannot make it work in other way. Can someone change my implementation to a ListView.builder? I am stuck a little bit :(
Key goal:
Idea:
Click on a button -> form appear -> after you press a submit button list is updated instantly
I'll add a delete function later, just learning docs, nothing more.
Can someone review my code and, if no one mind, try to rewrite the same idea, but using ListView.builder?
I've tried several times, but cannot get properties correctly from the form, and update listtile using builder, need help
Cheers!
ListView.builder requires a static height, so keep a track on that one. Now, coming to the question, that you want to use ListView.builder. You can do via this
Container(
height: give_your_height,
child: ListView.builder(
shrinkWrap: true,
itemCount: dataLists.lists.length,
itemBuilder: (context, index) {
return dataLists.lists[index];
}
)
)
Try this, it may solve your issue.
ListView(
children: [
for (ListTile e in dataLists.lists)
Card(child: e)
],
),
or with ListView.builder()
ListView.builder(
itemCount: dataLists.lists.length,
itemBuilder: (context, index) {
return dataLists.lists[index];
},
);
Further Reference: https://api.flutter.dev/flutter/material/ListTile-class.html

Flutter: how to show number of items in List

While learning Flutter, I have written a CRUD program that shows a list of items. I want to show at the bottom of the screen the number of items in the list, but I have been unable to achieve that. Currently the code shown contains (at the end) a BottomNavigationBar and a BottomNavigationBarItem where I attempt to show the number of items in the list, viz:
title: Text("Items = $this.itemCount")), // title: Text("")),
However it just shows "..." for the number of items. I would appreciate someone showing me how to achieve what I require.
class NotesList extends StatefulWidget {
#override
NotesListPageState createState() => NotesListPageState();
}
class NotesListPageState extends State<NotesList> {
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('Notes List'),
centerTitle: true,
),
body: new Container(
padding: new EdgeInsets.all(16.0),
child: new FutureBuilder<List<Map>>(
future: fetchDataFromDb(),
builder: (context, snapshot) {
if (snapshot == null) {
return Container(
alignment: AlignmentDirectional.center,
child: CircularProgressIndicator(),
);
} else if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot == null ? 0 : snapshot.data.length,
itemBuilder: (context, index) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
ListTile(
leading: (IconButton /* Edit */ (
color: Colors.blue,
icon: new Icon(Icons.edit),
onPressed: () => _showEditScreen(
Crud.eUpdate, snapshot.data[index]))),
onLongPress: () => _showEditScreen(
Crud.eRead, snapshot.data[index]),
trailing: (IconButton(
color: Colors.red,
icon: new Icon(Icons.delete),
onPressed: () => _showEditScreen(
Crud.eDelete, snapshot.data[index])))),
]);
});
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
} else {
return new Text("No data in table");
}
},
),
),
bottomNavigationBar: BottomNavigationBar(
onTap: (int index) {
if (index == 1) {
Navigator.of(context).pop();
}
},
items: [
BottomNavigationBarItem(
icon: Icon(Icons.info),
title: Text("Items = $this.itemCount")), // title: Text("")),
BottomNavigationBarItem(
icon: Icon(Icons.add),
title: Text('Create'),
),
],
));
}
'''
In your state class add at top
int count = 0;
add the code
WidgetsBinding.instance
.addPostFrameCallback((_) {
setState(){
count = snapshot.data.length;
}
});
between these two lines
else if (snapshot.hasData) {
return ListView.builder(
and change the title property to
title: Text("Items = $count")),
Show your text like this -
title: Text("Items = ${this.itemCount}")),
or like -
title: Text("Items = " + this.itemCount)),