How to open a drawer when the application starts? - flutter

My app load on started page, and when my page is loaded me need automacly show drawer menu, how can I open this? in void main(), have this:
new MaterialApp(
initialRoute: '/page',
builder: (context, widget) {
return new Padding(
child: widget,
padding: new EdgeInsets.only(bottom: 10.0),
);

void main() => runApp(MaterialApp(home: AppPage()));
class AppPage extends StatefulWidget {
#override
_AppPageState createState() => _AppPageState();
}
class _AppPageState extends State<AppPage> {
final GlobalKey<ScaffoldState> _key = GlobalKey();
#override
void initState() {
super.initState();
Timer.run(() => _key.currentState.openDrawer());
}
#override
Widget build(BuildContext context) {
return Scaffold(
key: _key,
appBar: AppBar(),
drawer: Drawer(),
);
}
}

Related

How to Maintain State of Drawer's Child Widget

I have a stateful widget, Counter, with a button and a counter that keeps track of how many times the button was pressed. This widget is in the drawer. When I close and open the drawer again, the counter is reset. How do I make it so that the counter is not reset upon closing and opening the drawer?
Here is the code:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
drawer: Drawer(
child: Counter(),
),
appBar: AppBar(),
body: Container(),
),
);
}
}
class Counter extends StatefulWidget {
const Counter({super.key});
#override
State<Counter> createState() => _CounterState();
}
class _CounterState extends State<Counter> {
int _count = 0;
#override
Widget build(BuildContext context) {
return Column(
children: [
Text(_count.toString()),
ElevatedButton(
onPressed: () {
setState(() {
_count = _count + 1;
});
},
child: Text('Increment Counter'),
)
],
);
}
}
To keep state of a variable within the Drawer(), the real solution would be to use a State Management library.
However, what you can do is create a global variable and pass it down the tree to Drawer():
import 'package:flutter/material.dart';
void main() {
runApp( MyApp());
}
class MyApp extends StatelessWidget {
var counter = 0;
MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
drawer: Drawer(
child: Counter(counter: counter,),
),
appBar: AppBar(),
body: Container(),
),
);
}
}
class Counter extends StatefulWidget {
int counter;
Counter({required this.counter,super.key});
#override
State<Counter> createState() => _CounterState();
}
class _CounterState extends State<Counter> {
#override
Widget build(BuildContext context) {
return Column(
children: [
Text(widget.counter.toString()),
ElevatedButton(
onPressed: () {
setState(() {
widget.counter = widget.counter + 1;
});
},
child: Text('Increment Counter'),
)
],
);
}
}

Creating a calendar in Second Route widget in Flutter

I am very new to using Flutter and can't figure out how to include a calendar on a widget I am using as the 'second route'.
class FirstRoute extends StatefulWidget {
FirstRoute({Key key, this.title}) : super(key: key);
final String title;
#override
_FirstRoute createState() => _FirstRoute();
}
class _FirstRoute extends State<FirstRoute> {
CalendarController _controller;
#override
void initState(){
super.initState();
_controller = CalendarController();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Project'),
),
body: new Center(
child: new ListView(
children: <Widget>[
TableCalendar(calendarController: _controller,)
...
Currently, this works to show the calendar on the first page but I am wanting it on the second page which I have as:
class SecondRoute extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Second Route"),
),
body: SingleChildScrollView(
child: new ListView(
children: <Widget>[
...
And navigate to using:
...
ListTile(
title: Text('Calendar'),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondRoute())
);
},
),
...
Is there a way of using the TableCalendar(calendarController: _controller,) in the second route? I tried to add of the calendar code into the second route but this does not work as you can't use the initState() in a widget. Thank you!
In you want to use initState in the SecondRoute page, it must extend StatefulWidget as following
class SecondRoute extends StatefulWidget {
#override
_SecondRoute createState() => _SecondRoute();
}
class _SecondRoute extends State<SecondRoute> {
CalendarController _controller;
#override
void initState(){
super.initState();
_controller = CalendarController();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Second Route"), ),
body: ListView(
children: [
TableCalendar(calendarController: _controller,)
],
),
),
}
}

How to use DrawerControllerState in a Drawer?

Minimal code:
void main() => runApp(MaterialApp(home: MainPage()));
class MainPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
drawer: MyDrawer(),
);
}
}
class MyDrawer extends StatelessWidget {
final _drawerKey = GlobalKey<DrawerControllerState>();
#override
Widget build(BuildContext context) {
return Drawer(
key: _drawerKey,
child: RaisedButton(
onPressed: () => print(_drawerKey.currentState), // Prints null
child: Text('Show Dialog'),
),
);
}
}
When I press the button, it prints null, so what's the correct way of using the DrawerControllerState?
See, the problem is, you are not using your key correctly. Firstly, in order to get the state of the Drawer, you need to have ScaffoldState not the DrawerControllerState type key.
With the use of ScaffoldState.currentState, you will be getting the data. Also, if you want to see whether your drawer is open or closed. You can use it like this:
ScaffoldState.currentState.isDrawerOpen
There are two ways to do this:
1. Declaring the GlobalKey gloabally for accessing it anywhere
void main() => runApp(MaterialApp(home: MainPage()));
// declare it globally or make your drawer inside the MainPage only
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
class MainPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey, // <-- Use your key here not for drawer
appBar: AppBar(),
drawer: MyDrawer(),
);
}
}
class MyDrawer extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Drawer(
child: RaisedButton(
onPressed: () => print(_scaffoldKey.currentState.isDrawerOpen), // <-- prints true, when open
child: Text('Show Dialog'),
)
);
}
}
2. Make your drawer inside your MainPage only. Easily accessible
class MainPage extends StatelessWidget {
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
Drawer get _drawer => Drawer(
child: RaisedButton(
onPressed: () => print(_scaffoldKey.currentState.isDrawerOpen), // <-- prints true when opened
child: Text('Show Dialog'),
)
);
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey, // <-- Using Key for Scaffold
appBar: AppBar(),
drawer: _drawer
);
}
}
Passing the GlobalKey from MainPage to MyDrawer won't help. You can play with that.

how to stop navigator to reload view?

I'm new to flutter and have a question about navigator.
I have 2 views one called Home and List. I created a drawer that is persistent in these two views. In each view I'm creating a reference to Firebase using FutureBuilder. The problem I'm running into is that every time I go to either Home or List initState is being called again. I believe the problem comes from selecting the page from the drawer. My question How can I still move to different pages without having to called InitState everytime I change screens.
title: Text('Go to page 1'),
onTap: () {
Navigator.of(context)
.push(MaterialPageRoute(builder: (context) => Listdb()));
This is where I think the screen rebuilds itself. Is there a way to avoid rebuilding?
Thank you for your help!
You can use the AutomaticKeepAliveClientMixin to prevent reloading everytime you change page, combining with PageView for better navigation. I'll included an example here:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final PageController _pageController = PageController();
Widget build(BuildContext context) {
return Scaffold(
drawer: Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: <Widget>[
DrawerHeader(
child: Text('Drawer Header'),
decoration: BoxDecoration(
color: Colors.blue,
),
),
ListTile(
title: Text('Item 1'),
onTap: () {
_pageController.jumpToPage(0);
Navigator.pop(context);
},
),
ListTile(
title: Text('Item 2'),
onTap: () {
_pageController.jumpToPage(1);
Navigator.pop(context);
},
),
],
),
),
body: PageView(
controller: _pageController,
children: <Widget>[
PageOne(),
PageTwo(),
],
),
);
}
}
class PageOne extends StatefulWidget {
#override
_PageOneState createState() => _PageOneState();
}
class _PageOneState extends State<PageOne> with AutomaticKeepAliveClientMixin {
#override
void initState() {
print("From PageOne - This will only print once");
super.initState();
}
#override
bool get wantKeepAlive => true;
#override
Widget build(BuildContext context) {
super.build(context);
return Scaffold(
backgroundColor: Colors.red,
);
}
}
class PageTwo extends StatefulWidget {
#override
_PageTwoState createState() => _PageTwoState();
}
class _PageTwoState extends State<PageTwo> with AutomaticKeepAliveClientMixin {
#override
void initState() {
print("From PageTwo - This will only print once");
super.initState();
}
#override
bool get wantKeepAlive => true;
#override
Widget build(BuildContext context) {
super.build(context);
return Scaffold(
backgroundColor: Colors.blue,
);
}
}

Flutter : Navigator operation requested with a context that does not include a Navigator

I have a scenario wherein I check the value of SharePreferences based on the value it will redirect the user to HomePage or LandingPage. I am not sure where did I got wrong? but I am getting this error below: I guess its not getting the context right any idea how do I get it?.
Unhandled Exception: Navigator operation requested with a context that does not include a Navigator.
E/flutter (11533): The context used to push or pop routes from the Navigator must be that of a widget that is a descendant of a Navigator widget.
Here is my code:
import 'package:credit/src/pages/landing.dart';
import 'package:flutter/material.dart';
import 'package:credit/src/pages/credit/home.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
MyApp({Key key}) : super(key: key);
_LoadingPageState createState() => _LoadingPageState();
}
class _LoadingPageState extends State<MyApp> {
#override
void initState() {
super.initState();
getUserStatus().then((userStatus) {
if (userStatus == null) {
Navigator.of(context)
.push(MaterialPageRoute<Null>(builder: (BuildContext context) {
return LandingPage();
}));
} else {
Navigator.of(context)
.push(MaterialPageRoute<Null>(builder: (BuildContext context) {
return HomePage();
}));
}
});
}
#override
Widget build(BuildContext context) {
return Container(
child: Center(
child: CircularProgressIndicator(),
));
}
}
Future<String> getUserStatus() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String userStatus = prefs.getString('userstatus');
print("==On Load Check ==");
print(userStatus);
return userStatus;
}
When you call Navigator.of(context) framework goes up in widget tree attached to provided context and tries to find the closest Navigator.
The widget tree you showed does not have one, so you need to include Navigator in the widget tree.
Easiest option is to use MaterialApp with your widget passed as home. MaterialApp is creating navigator inside itself. (CupertinoApp does it too)
Updated code from original example:
import 'package:credit/src/pages/landing.dart';
import 'package:flutter/material.dart';
import 'package:credit/src/pages/credit/home.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
MyApp({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
home: LoadingPage(),
);
}
}
class LoadingPage extends StatefulWidget {
LoadingPage({Key key}) : super(key: key);
_LoadingPageState createState() => _LoadingPageState();
}
class _LoadingPageState extends State<LoadingPage> { // note type update
#override
void initState() {
super.initState();
getUserStatus().then((userStatus) {
if (userStatus == null) {
Navigator.of(context)
.push(MaterialPageRoute<Null>(builder: (BuildContext context) {
return LandingPage();
}));
} else {
Navigator.of(context)
.push(MaterialPageRoute<Null>(builder: (BuildContext context) {
return HomePage();
}));
}
});
}
#override
Widget build(BuildContext context) {
return Container(
child: Center(
child: CircularProgressIndicator(),
));
}
}
Future<String> getUserStatus() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String userStatus = prefs.getString('userstatus');
print("==On Load Check ==");
print(userStatus);
return userStatus;
}
I have changed my code from
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Demo App',
theme: ThemeData(
primarySwatch: white,
scaffoldBackgroundColor: Colors.white,
),
home: Scaffold(
appBar: AppBar(
title: Text('Demo App'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
HomeScreen(title: 'Demo Home')));
},
child: Text('Open Home Screen'))
],
),
),
),
);
}
To
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Demo App',
theme: ThemeData(
primarySwatch: white,
scaffoldBackgroundColor: Colors.white,
),
home: InitScreen());
}
}
class InitScreen extends StatelessWidget {
const InitScreen({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Demo App'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
HomeScreen(title: 'Demo Home')));
},
child: Text('Open Home Screen'))
],
),
),
);
}
What changed?
Create a separate widget for home code in MyApp with InitScreen
What was the issue?
When we try to push Route by using Navigator.of(context), flutter will
try to find Navigator in the widget tree of the given context. In the
initial code, there was no widget that has Navigator. So, create a
separate widget for home code. And the MaterialApp widget in MyApp
will have Navigator.