How does one implement a menu widget on iOS equivalent to the Android (Material) drawer in Flutter?
There is no straightforward alternative menu widget in iOS, because Apple doesn't recommend drawers at all.
A drawer is a type of interface element that contains options or information
related to a specific window. A drawer is hidden by default, and can
only be revealed when the parent window is visible on screen. When
revealed, typically in response to a button, menu, or action, the
drawer slides out from one of side of the parent window. A drawer
can’t be moved separately or detached from its parent window.
Avoid using drawers. Drawers are seldom used in modern apps and their
use is discouraged. Panels, popovers, sidebars, and split views are
preferred for displaying supplementary window content.
Quick solution
Example
The Flutter team has prepared a comprehensive example of Android / iOS multiplatform app design.
You can build this yourself. You can use Positioned and GestureDetector, for example. Than you can animate it's moving. Is done very easy.
If you really want, you can workaround by defining a page that you use in the drawer for Android and as just another page for iOS:
Widget settingsWheel = Platform.isAndroid
? Builder(builder: (BuildContext context) => IconButton(
icon: settingsIcon,
onPressed: () => Scaffold.of(context).openDrawer(),
tooltip: "Open App Settings",
))
: Builder(builder: (BuildContext context) => CupertinoButton(
child: settingsIcon,
onPressed: () => Navigator.pushNamed(context, AppSettings.pageRoute),
));
...and then give it a "drawer-ish" feel by sliding the page in from the left:
onGenerateRoute: (route){
if (route.name == AppSettings.pageRoute) {
return PageTransition(
child: AppSettings(),
type: PageTransitionType.leftToRight,
settings: route,);
}
Simply place the Drawer in the endDrawer attribute of Scaffold, example:
Drawer(
child: ListView(shrinkWrap: true,
children: <Widget>[
UserAccountsDrawerHeader(
decoration: BoxDecoration(
color: myColour,
),
accountName:
Padding(child: Row(
children: <Widget>[
Text("Marcelo Augusto Elias"),
IconButton(
icon: Icon(
Icons.edit,
color: whiteColour,
),
onPressed: () {},
),
],
), padding: EdgeInsets.only(top: 10),),
accountEmail: Text("N° Cartão: XXXX XXXX XXXX 5154"),
currentAccountPicture: CircleAvatar(
backgroundColor:
Theme.of(context).platform == TargetPlatform.iOS
? myColour
: Colors.white,
child: Icon(
Icons.person,
size: 50,
),
),
),
ListTile(
dense: true,
title: Text("Fatura"),
leading: new Image.asset(
"assets/images/icon_fatura_barra_menu.png",
width: 20.0,
),
onTap: () {
Navigator.pop(context);
Navigator.push(
context,
MaterialPageRoute(builder: (context) => Fatura()),
);
},
),
ListTile(
onTap: () {
Navigator.pop(context);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ConsultaMargem()),
);
},
dense: true,
title: Text("Extrato"),
leading: new Image.asset(
"assets/images/icon_barra_menu_extrato.png",
width: 20.0,
),
),
ListTile(
onTap: () {
Navigator.pop(context);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DesbloquearPrimeiraVia()),
);
},
dense: true,
title: Text("Desbloquear Cartão"),
leading: new Image.asset(
"assets/images/icon_barra_menu_desbloquearcartao.png",
width: 24.0,
),
),
ListTile(
dense: true,
title: Text("Meu Cartão"),
leading: new Image.asset(
"assets/images/icon_barra_menu_meucartao.png",
width: 20.0,
),
onTap: () {
Navigator.pop(context);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Contracheques()),
);
},
),
ListTile(
onTap: () {
Navigator.pop(context);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => HistoricoConsignacoes()),
);
},
dense: true,
title: Text("Adiantamento Salarial"),
leading: new Image.asset(
"assets/images/icon_barra_menu_adiantamentosalarial.png",
width: 20.0,
),
),
/* ListTile(
onTap: () {
Navigator.pop(context);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ConsultaMargem()),
);
},
dense: true,
title: Text("Consulta de Margem"),
leading: new Image.asset(
"assets/images/icon_consulta_margem.png",
width: 20.0,
),
), */
/* ListTile(
dense: true,
title: Text("Informe de Rendimentos"),
leading: new Image.asset(
"assets/images/icon_rendimento.png",
width: 20.0,
),
), */
/* ListTile(
onTap: () {
Navigator.pop(context);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SimularEmprestimos()),
);
},
dense: true,
title: Text("Simular Empréstimo"),
leading: new Image.asset(
"assets/images/Icon_cal.png",
width: 20.0,
),
), */
Divider(),
ListTile(
dense: true,
title: Text("Compartilhar"),
leading: new Image.asset(
"assets/images/icon_compartilhar.png",
width: 20.0,
),
),
ListTile(
dense: true,
title: Text("Avaliar"),
leading: new Image.asset(
"assets/images/icon_estrela.png",
width: 20.0,
),
),
Divider(),
ListTile(
onTap: () {
Navigator.pop(context);
},
dense: true,
title: Text("Sair"),
trailing: Text(
"Versão 2.0",
style:
TextStyle(fontWeight: FontWeight.bold, fontSize: 12),
),
leading: new Image.asset(
"assets/images/icon_porta_sair.png",
width: 20.0,
),
),
],
),
)
Related
Please help me!!!
I m new in flutter, i want to create like this in my flutter app on button click.
Try below code,
showModalBottomSheet(
context: context,
builder: (BuildContext cntx) {
return Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
ListTile(
leading: Icon(Icons.camera),
title: Text("Camera"),
onTap: () async {
},
),
ListTile(
leading: Icon(Icons.image),
title: Text("Files")),
onTap: () async {
},
),
Container(
height: 50,
color: prefix0.appBackgroundColcor,
child: ListTile(
title: Center(
child: Text(
"Cancel",
style: TextStyle(color: Colors.white),
),
),
onTap: () {
Navigator.pop(context);
},
),
)
],
);
});
You can use cupertino bottom modal to achieve something like that. See below code for implementation:
void show() {
showCupertinoModalPopup(
context: context,
builder: (BuildContext cont) {
return CupertinoActionSheet(
actions: [
CupertinoActionSheetAction(
onPressed: () {
print('Camera');
},
child: Text('Use Camera'),
),
CupertinoActionSheetAction(
onPressed: () {
print('Upload files');
},
child: Text('Upload from files'),
),
CupertinoActionSheetAction(
onPressed: () {
print('Dropbox');
},
child: Text('Upload from DropBox'),
)
],
cancelButton: CupertinoActionSheetAction(
onPressed: () {
Navigator.of(cont).pop;
},
child: Text('Cancel', style: TextStyle(color: Colors.red)),
),
);
});
}
Call the show function with the press of any button. You will see something like this.
If you don't want the divider between actions you have to create your own custom popup.
I am working on an flutter application where it has more than 5 screens, I want to handle the back button of android, for example, when I logged in on app, it display the dashboard screen, so when I move to profile and then move to history screen, and when I click on back button on history screen it should navigate to profile screen, because the last screen I visited before history screen is profile, but it display the first screen which login screen.
I found the solution which works like when I click on back button it close the app.
Update:
My screens are navigating from drawer and from bottom navigation, there is only login screen where i use login button and calling dashboard screen onpressed function, other than this there is no button on any screen which navigate to other screens. here is the code for drawer and bottom navigation.
this is line of code i am using on login button
Navigator.push(
context, new MaterialPageRoute(builder: (context) => Employee()));
drawer and bottom navigation code:
Code:
class Employee extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
debugShowCheckedModeBanner: false,
theme: new ThemeData(
primaryColor: Colors.blue
),
home: EmployeeNavigation(),
);
}
}
int _selectedTab = 0;
final _pageOptions = [
EmployeeDashboard(),
location(),
Profile()
];
String getname="";
String getemail="";
String getdesignation="";
String getaccesstoken="";
String getdate;
String getTime;
// ignore: must_be_immutable
class EmployeeNavigation extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return EmployeeNavigationState();
}
}
class EmployeeNavigationState extends State<EmployeeNavigation> {
var email;
var designation;
var date;
bool valuefirst = false;
String backtext="";
#override
Widget build(BuildContext context) {
//i used this too but it doesn't work.
return WillPopScope(
onWillPop: () async {
if (_selectedTab == 0) {
return true;
}
setState(() {
_selectedTab = 0;
});
return false;
},
child:Scaffold(
drawer:Emp_DrawerCode(),
body: _pageOptions[_selectedTab],
bottomNavigationBar: BottomNavigationBar(
backgroundColor: Colors.blue[50],
type: BottomNavigationBarType.fixed,
currentIndex: _selectedTab,
onTap: (value) {
print(value);
setState(() {
_selectedTab = value;
});
},
items: [
BottomNavigationBarItem(icon: Icon(Icons.home), label: "Home"),
BottomNavigationBarItem(icon: Icon(Icons.location_on), label: "Location"),
BottomNavigationBarItem(
icon: Icon(Icons.person),
label: "Profile",
),
],
)));
}
}
class Emp_DrawerCode extends StatefulWidget {
#override
_Emp_DrawerCodeState createState() => _Emp_DrawerCodeState();
}
class _Emp_DrawerCodeState extends State<Emp_DrawerCode> {
SharedPreferences myPrefs;
name() async{
myPrefs=await SharedPreferences.getInstance();
setState(() {
getname=myPrefs.getString('name');
getemail=myPrefs.getString('email');
getdesignation=myPrefs.getString('designation');
});
}
void initState(){
name();
}
#override
Widget build(BuildContext context) {
return new Drawer(
child: new ListView(
padding: const EdgeInsets.all(0.0),
children: <Widget>[
new UserAccountsDrawerHeader(
accountName: new Text(getname),
accountEmail: new Text(getemail),
currentAccountPicture: new CircleAvatar(
backgroundColor:
Theme.of(context).platform == TargetPlatform.android
? Colors.white
: Colors.blue,
child: Text(
getname[0][0],
style: TextStyle(fontSize: 40.0),
),
),
),
new ListTile(
title: new Text('Home'),
leading: Icon(Icons.dashboard,color:Colors.grey),
onTap: (){
Navigator.pop(context);
Navigator.push(context, new MaterialPageRoute(
builder: (context)=> EmployeeNavigation()
)
);
},
),
new ListTile(
title: new Text('Request for leave'),
leading: Icon(Icons.request_page,color:Colors.grey),
onTap: (){
Navigator.pop(context);
Navigator.push(context, new MaterialPageRoute(
builder: (context)=>RequestForLeave()
)
);
},
),
new ExpansionTile(
title: new Text('History'),
children: <Widget>[
ListTile(
title:new Text("My Attendance"),
leading: Icon(Icons.assessment_outlined ,color:Colors.grey),
onTap: (){
Navigator.pop(context);
Navigator.push(context, new MaterialPageRoute(
builder: (context)=>new MyAttendance()
)
);
},
),
ListTile(
title:new Text("Leaves"),
leading: Icon(Icons.assessment_outlined,color:Colors.grey ),
onTap: (){
Navigator.pop(context);
Navigator.push(context, new MaterialPageRoute(
builder: (context)=>new LeaveHistory()
)
);
},
),
],
leading: Icon(Icons.history,),
),
new ListTile(
title: new Text('Log out'),
leading: Icon(Icons.logout,color:Colors.grey),
onTap: (){
myPrefs.setBool('login', true);
Navigator.pop(context);
Navigator.push(context, new MaterialPageRoute(
builder: (context)=>
Login()
)
);
},
),
],
),
);
}
}
kindly please help how to do this.
I am using navigator.push method and it is acting as you want.
appBar: AppBar(
leading: IconButton(
icon: Icon(Icons.arrow_back, color: Colors.blue),
onPressed: () => Navigator.of(context).pop(),
),
title: Text("Sample"),
centerTitle: true,
),
I wish it solve your problem.
You need to first clarify your question a bit more.
Are using page view/ are you talking about a scenario where all are separately navigable screens or some different scenario ?
I am considering it as the second scenario when all are separately navigable screens.
In that case, every time user navigates to next screen you must use Navigator.pushNamed() / Navigator.push() as for now I think you are using Navigator.pushReplacement() which is causing this issue probably.
Navigator is nothing but a class aware of the stack of screens in the memory and so are the functions it provides us with. A simple push would mean pushing over the last pushed screen whereas pushing a replacement would replace the last pushed screen ultimately preventing you from navigating to the last pushed screen. Exactly like how it would work for a stack data structure.
Firstly Wrap your Scaffold with WillPopScope
return WillPopScope(
onWillPop: _onBackPressed,
child : Scaffold());
And then you can call the Function that handles the back press.
// Back Button Android Behaviour
Future<bool> _onBackPressed() async {
final shouldPop = await showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text(
"Are you sure you want to leave this page?",
style: TextStyle(
color: Colors.black,
fontSize: 25.0,
fontWeight: FontWeight.w500,
),
),
actions: <Widget>[
SizedBox(width: 16),
InkWell(
onTap: () {
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (BuildContext context) => HomeScreen(), // Destination
),
(route) => false,
);
},
child: Container(
padding: EdgeInsets.all(8.0),
child: Text(
"LEAVE",
style: TextStyle(
color: Colors.red,
fontSize: 20.0,
fontWeight: FontWeight.w500,
),
),
),
),
SizedBox(width: 8.0),
InkWell(
onTap: () => Navigator.of(context).pop(false),
child: Container(
padding: EdgeInsets.all(8.0),
child: Text(
"DO NOT LEAVE",
style: TextStyle(
color: Colors.black,
fontSize: 20.0,
fontWeight: FontWeight.w500,
),
),
),
),
],
));
return shouldPop ?? false;
}
I am developing a flutter app that has a customer listing and a "Add" Floating button. When i click on Add button , I am taking it to a new screen for adding new customer. In this screen, when device back button is pressed, It should go back to the listing screen. But, instead it is going back to Dashboard.(App Flow : Dashboard screen(Customers) -> Customer Listing screen -> Add customer Screen.
I have already tried the Willscope method :
My Add Customer Screen:
Widget build(BuildContext context) {
Future<bool> _onBackPressed() async {
return (await showDialog(
context: context,
builder: (context) => new AlertDialog(
title: new Text('Are you sure?'),
content: new Text('Do you want to exit Add Customer'),
actions: <Widget>[
new FlatButton(
onPressed: () => Navigator.of(context).pop(false),
child: new Text('No'),
),
new FlatButton(
onPressed: () => Navigator.of(context).pop(true),
child: new Text('Yes'),
),
],
),
)) ?? false;
}
return new
WillPopScope(
onWillPop: _onBackPressed,
child : new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
backgroundColor: theme_color,
),
body: new SafeArea(
top: false,
bottom: false,
child: new Form(
key: _formKey,
autovalidate: true,
child: new ListView(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
children: <Widget>[
SizedBox(
height: 20,
),
//Form Elements
new Container(
height: 70,
padding: const EdgeInsets.only(
left: 10.0, right: 10.0, top: 20.0),
child: new RaisedButton(
color: theme_color,
child: const Text(
'Save',
style: TextStyle(color: Colors.white),
),
onPressed: () {
//Send Data to Database
}
},
)), //Save button
],
))),
));
}
Use this , Instead of return anything .
Future<void> _onBackPressed() async {
return (await showDialog(
context: context,
builder: (context) => new AlertDialog(
title: new Text('Are you sure?'),
content: new Text('Do you want to exit Add Customer'),
actions: <Widget>[
new FlatButton(
onPressed: () => Navigator.of(context).pop(false),
child: new Text('No'),
),
new FlatButton(
onPressed: () => Navigator.of(context).pop(true),
child: new Text('Yes'),
),
],
),
));
}
I created an app drawer and now I need to add color to its background. I managed to add color to the header part now I'm struggling to add to the bottom part. My color choices are RED and Golden Yellow. I am a little bit new to flutter and still learning through by making this kind of projects. And if you have any suggestions regarding making app drawer like this or any other ways I like to know those too.
Here is my appDrawer.dar code
import 'package:flutter/material.dart';
class NavDrawer extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Drawer(
elevation: 20.0,
child: ListView(
padding: EdgeInsets.zero,
children: [
DrawerHeader(
child: Text(
'Hello Welcome!',
style: TextStyle(color: Colors.white, fontSize: 25),
),
decoration: BoxDecoration(
color: Color.fromRGBO(225, 223, 0, 100),
)),
ListTile(
leading: Icon(Icons.input),
title: Text('Welcome'),
onTap: () => {},
),
ListTile(
leading: Icon(Icons.verified_user),
title: Text('Profile'),
onTap: () => {Navigator.of(context).pop()},
),
ListTile(
leading: Icon(Icons.settings),
title: Text('Settings'),
onTap: () => {Navigator.of(context).pop()},
),
ListTile(
leading: Icon(Icons.border_color),
title: Text('Feedback'),
onTap: () => {Navigator.of(context).pop()},
),
ListTile(
leading: Icon(Icons.exit_to_app),
title: Text('Logout'),
onTap: () => {Navigator.of(context).pop()},
),
],
),
);
}
}
I cold it likes this in my homeScreen.dart file
#override
Widget build(BuildContext context) {
return Scaffold(
extendBodyBehindAppBar: true, //this will extend map over the app bar
drawer: NavDrawer(),
appBar: AppBar(
centerTitle: true,
title: Text(
'MY ORDERS',
style: TextStyle(color: Colors.black, fontSize: 25.0),
),
You can wrap your widgets with container and give them a color property
Container(
color:Colors.red,
child:ListView(
padding: EdgeInsets.zero,
children: [
DrawerHeader(
child: Text(
'Hello Welcome!',
style: TextStyle(color: Colors.white, fontSize: 25),
),
decoration: BoxDecoration(
color: Color.fromRGBO(225, 223, 0, 100),
)),
ListTile(
leading: Icon(Icons.input),
title: Text('Welcome'),
onTap: () => {},
),
ListTile(
leading: Icon(Icons.verified_user),
title: Text('Profile'),
onTap: () => {Navigator.of(context).pop()},
),
ListTile(
leading: Icon(Icons.settings),
title: Text('Settings'),
onTap: () => {Navigator.of(context).pop()},
),
ListTile(
leading: Icon(Icons.border_color),
title: Text('Feedback'),
onTap: () => {Navigator.of(context).pop()},
),
ListTile(
leading: Icon(Icons.exit_to_app),
title: Text('Logout'),
onTap: () => {Navigator.of(context).pop()},
),
],
),
)
You can wrap your listview into a container and give color to that container. In that way, the color will be applied to the other items as well.
i cant seem to figure out how to make the burger icon bigger in my flutter app
here is the code for my flutter app, i am simply trying to make it bigger.....................................................................................................................................................
return new Scaffold(
backgroundColor: Colors.transparent,
appBar: AppBar(
backgroundColor: Colors.transparent,
elevation: 0.0,
iconTheme: new IconThemeData(color: Colors.black),
),
extendBodyBehindAppBar: true,
drawer: Drawer(
// Add a ListView to the drawer. This ensures the user can scroll
// through the options in the drawer if there isn't enough vertical
// space to fit everything.
child: ListView(
// Important: Remove any padding from the ListView.
padding: EdgeInsets.zero,
children: <Widget>[
DrawerHeader(
child: Text('Drawer Header', ),
decoration: BoxDecoration(
color: Colors.black,
),
),
ListTile(
title: Text('Trip History',style : TextStyle(fontSize: 20.0,fontFamily:"Clan-Medium")),
onTap: () {
// Update the state of the app
// ...
// Then close the drawer
Navigator.pop(context);
},
),
ListTile(
title: Text('Payment',style : TextStyle(fontSize: 20.0,fontFamily:"Clan-Medium")),
onTap: () {
// Update the state of the app
// ...
// Then close the drawer
Navigator.pop(context);
},
),
ListTile(
title: Text('Settings',style : TextStyle(fontSize: 20.0,fontFamily:"Clan-Medium")),
onTap: () {
// Update the state of the app
// ...
// Then close the drawer
Navigator.pop(context);
},
),
],
),
),
Change your AppBar to this and then focus on the size property of the Icon, Change it as you want.
appBar: AppBar(
backgroundColor: Colors.transparent,
elevation: 0.0,
iconTheme: new IconThemeData(color: Colors.black),
leading: IconButton(
icon: Icon(
Icons.menu,
color: Colors.black,
size: 24.0,
),
onPressed: (){
_scaffoldKey.currentState.openDrawer();
},
)
),
You can set any icon here.
Also, as the official docs say, you can use it as well which also has an option to set ToolTip.
AppBar(
leading: Builder(
builder: (BuildContext context) {
return IconButton(
icon: const Icon(Icons.menu),
onPressed: () { Scaffold.of(context).openDrawer(); },
tooltip: MaterialLocalizations.of(context).openAppDrawerTooltip,
);
},
),
)