Navigating to another page from drawer menu and setting title to app bar
Asked Answered
P

2

5

i am new to flutter and would like someone to help me with code i found in github that i would like to use. take a look at the link below https://github.com/JohannesMilke/drawer_example

this is an example of a navigational drawer. i like the way the developer coded it and would like to use this example. the problem is that the developer didnt implement navigating to another page. when you click on item in the drawer, it just print a message in the console.

i want to take this a step further. i want to modified the code so that when you click on a item it will navigate to another page and the drawer will b closed. the drawer icon should remain on the toolbar on the new page displayed. also, when you navigate to another page the title of that page should be set in the toolbar.

when i looked at the code , i have an idea where to change but i am not successful. i think i need to change the body tag at the bottom of the code. the problem is that i dont know how to call the DrawerWidgetState class in drawer_widget.dart file.


void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  final String appTitle =  'Ttitle';
  @override
  Widget build(BuildContext context) => MaterialApp(
    title: appTitle,
    theme: ThemeData(
      primaryColor: Colors.red,
      textTheme: TextTheme(
        subhead: TextStyle(
          color: Colors.black.withOpacity(0.4),
        ),
      ),
      dividerColor: Colors.black.withOpacity(0.4),
    ),
    home: MainPage(appTitle: appTitle),
  );
}

class MainPage extends StatefulWidget {
  final String appTitle;

  const MainPage({this.appTitle});

  @override
  MainPageState createState() => MainPageState();
}

class MainPageState extends State<MainPage> {
  @override
  Widget build(BuildContext context) => Scaffold(
    appBar: AppBar(
      title: Text(widget.appTitle),
    ),
    drawer: DrawerWidget(),
    body: container()
  );
}

i define the following function in drawer_widget.dart file

getDrawerItemWidget(int pos) {
    print('testing');
    switch (pos) {
      case 0:
        return new FirstFragment();
      case 1:
        return new SecondFragment();
      case 2:
        return new ThirdFragment();

      default:
        return new Text("Error");
    }
  }

but i dont know how to call it from Mainpage Body tag and set title accordingly. can someone help modify the code so that i can nagivate to another page and set title? full code is in https://github.com/JohannesMilke/drawer_example

thanks in advance

Pyrenees answered 17/6, 2019 at 19:0 Comment(1)
In that example you're using some kind of "Fragment Activities", therefore they're sharing the AppBar. You can change the title based on the selected "Fragment". But, is that what you want? Or do you want to Navigate entirely to a new page (push/pop approach)?Gould
W
3

Using the drawer_example library you need to make some small changes in order to make it work.

Over your drawer_widget.dart add this add the beginning:

typedef TitleCallback = void Function(String, int);

Once you do that, your Drawer StatefulWidget should looks this way:

class DrawerWidget extends StatefulWidget {

  final TitleCallback callback;
  final int tabIndex;

  @override
  DrawerWidgetState createState() => DrawerWidgetState();

  DrawerWidget(this.callback, this.tabIndex);
}

and your initState:

@override
void initState() {
    selectedDrawerIndex = widget.tabIndex;
    selectedProfileIndex = 0;
    super.initState();
}

This will be the constructor to pass the new value back to your main.dart file.

Inside the ListTile, you can add the following logic:

ListTile(
     leading: Icon(item.icon),
     title: Text(item.name),
     selected: selectedDrawerIndex == currentIndex,
     onTap: () {
         final item = getOffsetIndex(drawerGroups, currentIndex);
         print('Selected index $selectedDrawerIndex with name ${item.name}');

         setState(() {
            selectedDrawerIndex = currentIndex;
            widget.callback(item.name, selectedDrawerIndex);
         });
         Navigator.pop(context); // to close the Drawer
     },
)

If you can check, the line: widget.callback(item.name); sends the tab name over the callback and that logic can be applied any where you want to change your title. It can even be a hard coded title like:

widget.callback("Second Tab");

Now, going back to your main.dart file:

class MyApp extends StatefulWidget {
    final String title;

    ListExample(this.title);

  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {

   List<Widget> _fragments = <Widget> [
       Container(
         child: Text("Fragment One"),
       ),
       Container(
         child: Text("Fragment Two"),
       ),
       Container(
         child: Text("Fragment Three"),
       ),
   ];

  String titleAppBar = "Testing";
  int tabIndex = 0;

  @override
  void initState() {
    setState(() {
      titleAppBar = widget.title;
    });
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: widget.title,
      home: Scaffold(
        appBar: AppBar(
          title: Text(titleAppBar),
        ),
        drawer: DrawerWidget((title, index) {
          setState(() {
            titleAppBar = title;
            tabIndex = index;
          });
        }, tabIndex),
        body: _fragments[tabIndex],
      ),
    );
  }
}

Final Result:

Drawer Flutter

Winebibber answered 17/6, 2019 at 19:30 Comment(15)
The appTitle must be final, as it is a parameter passed to the StatefulWidget, not declared on the State.Gould
Thank you Mariano. the problem i am having is which file from the code link i provided should i change? i am not sure how to call variable in the drawer class from the main class. the main class is github.com/JohannesMilke/drawer_example/blob/master/lib/… and the drawer class github.com/JohannesMilke/drawer_example/blob/master/lib/widget/…Pyrenees
@MarianoZorrilla you should correct your answer. You're telling yoohoo to remove the final keyword, and that's wrong. You'll only create confusion to him (he said he's new to Flutter).Gould
@MarianoZorrilla what does this line do then? home: MainPage(appTitle: appTitle). It doesn't matter if he's passing a static value. StatefulWidget parameters must be final!Gould
@Pyrenees from the first one: github.com/JohannesMilke/drawer_example/blob/master/lib/… your can make the modifications as the AppBar Widget works inside your main.dart file.Winebibber
Thanks. i am still confused. if i modified the main dart file, still how do i call getDrawerItemWidget method in the main class if getDrawerItemWidget method is located in another class drawer_widget.dart. if you can, download the code and run with your changes.Pyrenees
@Pyrenees Please, check my updated response. That should work :)Winebibber
Thank you Mariano. this works great. there two things i notice. if will be great if you know how to do it. i noticed when you click on item and the drawer closes, the item doesnt stay highlighted in blue to indicate that was the last item clicked. only inbox is highlighted all the time even if you click Drafts. ideally, drafts should had been highlighted because that was the last item clicked. do you know how to fix that in the code? also, can you do a snippet of when you click on an item, it goes to another page and the menu should still appear in the toolbar?Pyrenees
@Pyrenees take a look now! I'm "saving" the index by passing the position back to the home and back to the drawer when you open it again. I gave it a try and works like you needed it :)Winebibber
@Pyrenees for that second request... I guess you can use a Navigator.push passing the current index (the one now storage) and then using that value to the new page and do your logic. Not sure if is recommended to have a new drawer to inner pages as you're routing inside a new view (like a index > description flow).Winebibber
thank again. the drawer should not be a new drawer. simply, when you click on an item, another page should be display for example(first_fragment.dart) while maintaining the same drawer in the toolbar. only the content,body, of the page should change. it should work like a fragment where only the content changes while the same drawer still in the toolbarPyrenees
@Pyrenees I see what you mean... I guess, if we're using this logic, every time you click on a tab, now that you have the index you should have compare it with a List<Widget> and get the index of your "fragment" (page) and display that one, kinda like the bottom navigation bar does.Winebibber
@Pyrenees take a look to my logic now. I have a List<Widget> which, instead of a Container could actually be your Fragment StatefulWidget or anything you like, then, using the index you clicked, you will select the correct Fragment to display over the body of the Scaffold. Hope that's what you needed and correctly :)Winebibber
thanks a lot. this is exactly what i was looking for. keep up the good workPyrenees
@Pyrenees my pleasure! I'm happy that helped :)Winebibber
G
3

Looking at the example on GitHub, it's overcomplicating something that's too easy with Flutter.

Here's a simple example on how to use a Drawer on Flutter:

main.dart

import 'package:flutter/material.dart';

import 'another_page.dart';
import 'home_page.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      // declaring your routes will allow you to push and remove everything from the stack (including the drawer) with pushNamedAndRemoveUntil()
      routes: {
        'home': (context) => HomePage(),
        'anotherPage': (context) => AnotherPage(),
      },
      initialRoute: 'home',
    );
  }
}

home_page.dart (another_page.dart is exactly the same for illustration purpose)

import 'package:flutter/material.dart';

import 'menu_drawer.dart';

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      drawer: MenuDrawer(),
      appBar: AppBar(
        title: Text('Home'),
      ),
      body: Center(
        child: Text('Home'),
      ),
    );
  }
}

menu_drawer.dart

import 'package:flutter/material.dart';

class MenuDrawer extends StatelessWidget {

  // Push the page and remove everything else
  navigateToPage(BuildContext context, String page) {
    Navigator.of(context).pushNamedAndRemoveUntil(page, (Route<dynamic> route) => false);
  }

  @override
  Widget build(BuildContext context) {
    return Drawer(
      child: ListView(
        // This could be mapped from a List of items
        children: <Widget>[
          ListTile(
            leading: Icon(Icons.home),
            title: Text('Home'),
            onTap: () => navigateToPage(context, 'home'),
          ),
          ListTile(
            leading: Icon(Icons.panorama),
            title: Text('Another page'),
            onTap: () => navigateToPage(context, 'anotherPage'),
          ),
        ],
      ),
    );
  }
}

Final result:

enter image description here

Gould answered 17/6, 2019 at 20:33 Comment(3)
It would be nice not know why the downvote. I'm giving a better alternative IMHO to what @Pyrenees needs to do.Gould
hi Gabriel, the author of the code i provided structured the code based on some programming standards. even though there are more files and looks complex, it may be easier to maintain and in my opinion looks very organize.Pyrenees
Hi @yoohoo, thanks for the feedback. I see your point and hope you find a solution soon. Still believe is not an elegant way of implementing a drawer. I'll leave this answer here anyway for other users reference. ;)Gould

© 2022 - 2024 — McMap. All rights reserved.