-
Notifications
You must be signed in to change notification settings - Fork 27.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Navigation with a drawer does not have closing animation #26954
Comments
So you want that when you click a link on drawer, just the main screen should be changed with no animation, and clicking back button should exit the app instead of going back to previous screen? |
yes like in for example in the Gmail App. But for me the back button is not really the problem. It's more the animation. And you don't really find any documentation about navigation with a drawer. |
To remove animation you can use PageRouteBuilder Navigator.push(context, PageRouteBuilder(
opaque: false,
pageBuilder: (context, _, __) {
return SecondScreen();
},
transitionsBuilder: (_, __, ___, Widget child) {
return child;
}
)); |
I tested it and for me it doesn't really looked nice. And in my eyes there must be a better solution. |
How does it not look nice? Perhaps you just want a https://docs.flutter.io/flutter/widgets/PageView-class.html instead of the Navigator. |
Maybe I am doing something wrong, but i created a test project and used what @Kartik1607 suggested. |
code: class StartPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('AppBar'),
),
drawer: TestDrawer(),
body: Center(
child: Text('drawer page'),
),
);
}
}
class FirstPage extends StatelessWidget {
final String text;
const FirstPage({Key key, this.text}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
body: Text(text),
appBar: AppBar(
title: Text(text),
),
drawer: TestDrawer(),
);
}
}
class TestDrawer extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Drawer(
child: ListView(
children: <Widget>[
ListTile(
title: Text('Item 1'),
onTap: () {
Navigator.of(context).pop();
Navigator.of(context).push(
PageRouteBuilder(pageBuilder: (context, _, __) {
return FirstPage(
text: 'Item 1',
);
}, transitionsBuilder: (_, __, ___, Widget child) {
return child;
}),
);
},
),
ListTile(
title: Text('Item 2'),
onTap: () {
Navigator.of(context).pop();
Navigator.of(context).push(
PageRouteBuilder(
pageBuilder: (context, _, __) {
return FirstPage(text: 'Item 2');
},
),
);
},
)
],
),
);
}
} |
What would need to be different for it to look nice? |
When you compare to the Gmail app you can clearly see that there's no real closing animation of the drawer. |
What you need is something like Nested/Child routes. This answer is what would help you. |
Thanks @Kartik1607 that's a good idea. I will take a look at it. But I wonder why there isn't a solution for my Problem, it's not that specific. |
The default drawer has the same animation like gmail. I didn't see any difference. |
But my previous video looks different than gmail ? |
@Kartik1607 I tried it with your solution, but there isn't a close animation like before. |
Do you know an open source example app that uses a drawer and a Scaffold for each Page ? |
Check the gallery app. Which version of flutter are you using? Maybe consider switching from |
I was thinking that since flutter is mostly favored towards composition ( eg Router(
Route(path:'/', widget:RootWidget()),
Route(path:'/details', widget:DetailsWidget()),
...
) This could solve problems of child router. But, I see some shortcomings, namely Widgets like Nevertheless, it would be nice to have router as a widget instead of a property. For the time being, I think there is a lack of resources for architecture of flutter app navigation. I'll try to take out some time and create some applications and write up an article. |
When using navigation drawer I came accros another problem. You cannot change the AppBar from a drawer page in an elegant way. On android you have |
I'd suggest to ask on StackOverflow how to do this in Flutter. |
That's not my problem. I think that flutter should provide either a guide or a component. @Kartik1607 could include something like that. |
I have the same problem with the drawer navigation. What I expected: After clicking on the new page button from the drawer, the drawer close with animation while the new page replace the original page in the background. The drawer should be on top of everything and shared by every page in the same route level. I am a bit disappointed with this while React Native can do this a lot better. Flutter should have something like Drawer Navigation, Stack Navigation and Tab Navigation. |
I am currently using this workaround. However, it is so complicated to change the app bar inside the "fragment". |
@kelvin1hui Did you figured out the way to change the scaffold content? |
@kelvin1hui @campanagerald you don't have to reuse the same Scaffold over your screens. |
Today I faced this problem too. Selecting an item in the drawer and updating the content while keeping the drawer in place. I found no good solution. Using onTap: () async {
// closes drawer.
Navigator.of(context).pop();
if (item.id != selectedItemId) {
await Future.delayed(Duration(milliseconds: 250), () {
onItemSelected(item.id);
});
}
}, Instead of pushing a new page I close the Drawer and then wait for the animation to finish (250 is a magic number that seemed about right) then I change what my build method returns using setState. And all pages that are open from the drawer sets a new scaffold and new drawer, and all of that pages lifts the It would be great if |
Did you try the solution above your message? |
I just did. I could use that approach, but I would have to disable the animations using |
For me the suggested solution above didn't worked. Instead of a ValueKey I now use the same GlobalKey for every Scaffold. To avoid duplicate GlobalKeys in the widget tree I set the transitionDuration of the PageRouteBuilder to zero. Although I have a solution to my problem, I will leave this issue open. There should be more documentation, something like 'navigating with drawer and routes'. |
This redraw animation seems strange to me too. I believe Flutter can incorporate something based on Android Fragments. |
Code Sample
flutter doctor -v
|
Based on comments and my testing, this can be improved. code sampleimport 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Material App',
theme: ThemeData.dark(),
home: StartPage(),
);
}
}
class Home extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Material App Bar'),
),
body: Center(
child: Container(
child: Text('Hello World'),
),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {},
),
);
}
}
class StartPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('AppBar'),
),
drawer: TestDrawer(),
body: Center(
child: Text('drawer page'),
),
);
}
}
class FirstPage extends StatelessWidget {
final String text;
const FirstPage({Key key, this.text}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
body: Text(text),
appBar: AppBar(
title: Text(text),
),
drawer: TestDrawer(),
);
}
}
class TestDrawer extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Drawer(
child: ListView(
children: <Widget>[
ListTile(
title: Text('Item 1'),
onTap: () {
Navigator.of(context).pop();
Navigator.of(context).push(
PageRouteBuilder(pageBuilder: (context, _, __) {
return FirstPage(
text: 'Item 1',
);
}, transitionsBuilder: (_, __, ___, Widget child) {
return child;
}),
);
},
),
ListTile(
title: Text('Item 2'),
onTap: () {
Navigator.of(context).pop();
Navigator.of(context).push(
PageRouteBuilder(
pageBuilder: (context, _, __) {
return FirstPage(text: 'Item 2');
},
),
);
},
)
],
),
);
}
} flutter doctor -v[✓] Flutter (Channel stable, 1.22.5, on macOS 11.1 20C69 darwin-x64, locale en-GB)
• Flutter version 1.22.5 at /Users/tahatesser/Code/flutter_stable
• Framework revision 7891006299 (6 weeks ago), 2020-12-10 11:54:40 -0800
• Engine revision ae90085a84
• Dart version 2.10.4
[✓] Android toolchain - develop for Android devices (Android SDK version 30.0.3)
• Android SDK at /Volumes/Extreme/SDK
• Platform android-30, build-tools 30.0.3
• ANDROID_HOME = /Volumes/Extreme/SDK
• Java binary at: /Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java
• Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6915495)
• All Android licenses accepted.
[✓] Xcode - develop for iOS and macOS (Xcode 12.3)
• Xcode at /Volumes/Extreme/Xcode.app/Contents/Developer
• Xcode 12.3, Build version 12C33
• CocoaPods version 1.10.1
[!] Android Studio (version 4.1)
• Android Studio at /Applications/Android Studio.app/Contents
✗ Flutter plugin not installed; this adds Flutter specific functionality.
✗ Dart plugin not installed; this adds Dart specific functionality.
• Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6915495)
[✓] VS Code (version 1.52.1)
• VS Code at /Applications/Visual Studio Code.app/Contents
• Flutter extension version 3.18.1
[✓] Connected device (1 available)
• Android SDK built for x86 (mobile) • emulator-5554 • android-x86 • Android 10 (API 29) (emulator)
! Doctor found issues in 1 category. [✓] Flutter (Channel master, 1.26.0-2.0.pre.402, on macOS 11.1 20C69 darwin-x64, locale en-GB)
• Flutter version 1.26.0-2.0.pre.402 at /Users/tahatesser/Code/flutter_master
• Framework revision 2a188eeca3 (10 hours ago), 2021-01-17 19:27:00 -0800
• Engine revision 609036f2bf
• Dart version 2.12.0 (build 2.12.0-236.0.dev)
[✓] Android toolchain - develop for Android devices (Android SDK version 30.0.3)
• Android SDK at /Volumes/Extreme/SDK
• Platform android-30, build-tools 30.0.3
• ANDROID_HOME = /Volumes/Extreme/SDK
• Java binary at: /Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java
• Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6915495)
• All Android licenses accepted.
[✓] Xcode - develop for iOS and macOS
• Xcode at /Volumes/Extreme/Xcode.app/Contents/Developer
• Xcode 12.3, Build version 12C33
• CocoaPods version 1.10.1
[✓] Chrome - develop for the web
• Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
[✓] Android Studio (version 4.1)
• Android Studio at /Applications/Android Studio.app/Contents
• Flutter plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/9212-flutter
• Dart plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/6351-dart
• Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6915495)
[✓] VS Code (version 1.52.1)
• VS Code at /Applications/Visual Studio Code.app/Contents
• Flutter extension version 3.18.1
[✓] Connected device (3 available)
• Android SDK built for x86 (mobile) • emulator-5554 • android-x86 • Android 10 (API 29) (emulator)
• macOS (desktop) • macos • darwin-x64 • macOS 11.1 20C69 darwin-x64
• Chrome (web) • chrome • web-javascript • Google Chrome 87.0.4280.141
• No issues found! |
I Verified the issue on the latest master channel, Navigation Drawer abruptly closes (Without slide transition) when navigating without any transition (Notice item 2).
But with the default code sampleimport 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Material App',
theme: ThemeData.dark(),
home: StartPage(),
);
}
}
class Home extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Material App Bar'),
),
body: Center(
child: Container(
child: Text('Hello World'),
),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {},
),
);
}
}
class StartPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('AppBar'),
),
drawer: TestDrawer(),
body: Center(
child: Text('drawer page'),
),
);
}
}
class FirstPage extends StatelessWidget {
final String text;
const FirstPage({Key? key,required this.text}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly ,
children: [
Text(text),
ElevatedButton(onPressed: () {
Navigator.of(context).pop();
}, child: Text('pop'))
],
),
),
appBar: AppBar(
title: Text(text),
),
drawer: TestDrawer(),
);
}
}
class TestDrawer extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Drawer(
child: ListView(
children: <Widget>[
ListTile(
title: Text('Item 1'),
onTap: () {
Navigator.of(context).pop();
Navigator.of(context).push(
MaterialPageRoute(builder: (context)=> FirstPage(text: "Item 1",) )
);
},
),
ListTile(
title: Text('Item 2'),
onTap: () {
Navigator.of(context).pop();
Navigator.of(context).push(
PageRouteBuilder(
pageBuilder: (context, _, __) {
return FirstPage(text: 'Item 2');
},
),
);
},
),
ListTile(
title: const Text('Fade Transition'),
onTap: () {
Navigator.of(context).pop();
Navigator.of(context).push(
PageRouteBuilder(
transitionsBuilder: (context, animation, secondaryAnimation, child) {
return FadeTransition(
opacity: animation,
child: child,
);
},
pageBuilder: (context, _, __) {
return FirstPage(text: 'Item 3');
})
);
},
),
ListTile(
title: Text('Slide Transition'),
onTap: () {
Navigator.of(context).pop();
Navigator.of(context).push(
PageRouteBuilder(
transitionsBuilder: (context, animation, secondaryAnimation, child) {
return SlideTransition(
position: Tween<Offset>(
begin: const Offset(1.0, 0),
end: Offset.zero,
).animate(animation),
// secondaryAnimation: secondaryAnimation,
child: child,
);
},
pageBuilder: (context, _, __) {
return const FirstPage(text: 'Item 4');
})
);
},
),
],
),
);
}
} author's code sampleimport 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Material App',
theme: ThemeData.dark(),
home: StartPage(),
);
}
}
class StartPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('AppBar'),
),
drawer: TestDrawer(),
body: Center(
child: Text('drawer page'),
),
);
}
}
class FirstPage extends StatelessWidget {
final String text;
const FirstPage({Key? key,required this.text}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
body: Text(text),
appBar: AppBar(
title: Text(text),
),
drawer: TestDrawer(),
);
}
}
class TestDrawer extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Drawer(
child: ListView(
children: <Widget>[
ListTile(
title: Text('Item 1'),
onTap: () {
Navigator.of(context).pop();
Navigator.of(context).push(
PageRouteBuilder(pageBuilder: (context, _, __) {
return FirstPage(
text: 'Item 1',
);
}, transitionsBuilder: (_, __, ___, Widget child) {
return child;
}),
);
},
),
ListTile(
title: Text('Item 2'),
onTap: () {
Navigator.of(context).pop();
Navigator.of(context).push(
PageRouteBuilder(
pageBuilder: (context, _, __) {
return FirstPage(text: 'Item 2');
},
),
);
},
)
],
),
);
}
} flutter doctor -v (mac)
|
@The-Redhat android-example.mp4I made It very similar to gmail (maybe even better for somebody) with just doing a delay in the button in the drawer. I'm using Navigator 2.0 (setHomeWidget function just calls setState on a parent widget updating the scaffold in navigator) and then calling Navigator.pop(context) inside the drawer which closes the drawer and after 200ms replaces the scaffold with a different one. Gmail has the delay longer but you can tinker with it and see what works for you. Here is a code sample: //call this from a button in a drawer
onTap: () async {
if (page == NavigationDrawerItem.automatickeObjednavky) {//if the user clicks the selected page
Navigator.pop(context);
} else {
Navigator.pop(context);//close the drawer
await Future.delayed(const Duration(milliseconds: 200));//wait for the drawer to close (or at least trigger the animation)
setHomeWidget(AutoPage(//switch to the different page
setHomeWidget: setHomeWidget,
));
}
}, Navigator 2.0 Implementationclass _MyAppState extends State<MyApp> {
void setHomeWidget(Widget widget) {
setState(() {
homeWidget = widget;
});
}
late Widget homeWidget;
@override
void initState() {
homeWidget = LoggingInWidget(setHomeWidget: setHomeWidget);
super.initState();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Navigator(
pages: [
MaterialPage(child: homeWidget),
],
onPopPage: (route, result) {
return route.didPop(result);
},
));
}
} In case you would need any details You can take a look at the whole app which I'm writing. It's opensource: https://github.com/tpkowastaken/autojidelna/tree/35987cdcbe1fd699664643a283456abd52d7b9bc You also said that the appbar is not easily set - You can just put the appbar widget into a stateful widget make it a variable and call setState on that variable. Other option is to use ValueListenableBuilder Which would only reload the specific thing and not the whole widget. Both are used in my app so you can also take a look there. Hope It helps! |
This issue is missing a priority label. Please set a priority label when adding the |
Making it possible to specify the NavigationDrawer's open and close animation durations and curves sounds like a resonable proposal. If someone wants to contribute a PR it would be worth considering how to compactly specify both the curve and reverse curve for both open and close, as well as the durations for both transitions. |
Hey,
I'm currently developing a flutter app. Because of its flexibility I use a navigation drawer. I came across multiple tutorials about navigation with a drawer in flutter, but no one was really satisfying. The first approach is to use one Scaffold with multiple layouts inside, like described here. But like one of the comments says it isn't a clean solution especially in a big app. Another approach is described here. It uses multiple Scaffolds and pushes them with
Navigator.of(context).push(...)
. Because of this method you have an animation between pages and are able to use the back button on android. So my question is, if there is a proper solution to use a navigation drawer. Maybe I'm just missing something and there's an example.Thanks in advance!
The text was updated successfully, but these errors were encountered: