flutterflutter-navigationflutter-drawer

How to create an expandable list in Flutter navigation drawer that uses single app bar for different screens


I need to develop a navigation drawer in flutter and I'm new to flutter, I am using the following code, and this is creating the menu as expected but the problem is

1.handling screen navigation 
2.maintaining state and navigating back to the screen which is previously opened

I am unable to use this code in stateful widget as i need to maintain the state of the navigation drawer

import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';

class ExpansionList extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ListView.builder(
          itemCount: data.length,
          itemBuilder: (BuildContext context, int index) => EntryItem(
            data[index],
          ),
        );
  }
}

// Welcome to another flutter tutorial
// In this video we will see how to create a multi-level Expansion List
// First Let's create a class for each row in the Expansion List

class Entry {
  final String title;
  final List<Entry>
  children; // Since this is an expansion list ...children can be another list of entries
  Entry(this.title, [this.children = const <Entry>[]]);
}

// This is the entire multi-level list displayed by this app
final List<Entry> data = <Entry>[
  Entry(
    'Chapter A',
    <Entry>[
      Entry('Section A0',
        // <Entry>[
        //   Entry('Item A0.1'),
        //   Entry('Item A0.2'),
        //   Entry('Item A0.3'),
        // ],
      ),
      Entry('Section A1'),
      Entry('Section A2'),
    ],
  ),
  // Second Row
  Entry('Chapter B', <Entry>[
    Entry('Section B0'),
    Entry('Section B1'),
  ]),
  Entry(
    'Chapter C',
    <Entry>[
      Entry('Section C0'),
      Entry('Section C1'),
      Entry(
        'Section C2',
        <Entry>[
          Entry('Item C2.0'),
          Entry('Item C2.1'),
          Entry('Item C2.2'),
          Entry('Item C2.3'),
        ],
      )
    ],
  ),
];

// Create the Widget for the row
class EntryItem extends StatelessWidget {
  const EntryItem(this.entry);

  final Entry entry;

  // This function recursively creates the multi-level list rows.
  Widget _buildTiles(Entry root) {
    if (root.children.isEmpty) {
      return ListTile(
        title: Text(root.title),
        onTap: (){
          Fluttertoast.showToast(msg: root.title);
          _getDrawerItemWidget(root.title);
        },
      );
    }
    return ExpansionTile(
      key: PageStorageKey<Entry>(root),
      title: Text(root.title),
      children: root.children.map<Widget>(_buildTiles).toList(),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Container(
        child: _buildTiles(entry));
  }

  _getDrawerItemWidget(String screenName) {
    switch (screenName) {
      case "Section A0":
        return new ThirdScreen();
      case "Section A1":
        return new SecondScreen();
      case "Section A2":
        return new ThirdScreen();

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

Basically I am an android app developer I'm looking forward to implement the following concept like sigle activity with navigation drawer and handling multiple fragments in flutter

Please help me to achieve the requirement

Any source code suggestions or fully implemented code is helpful for my need


Solution

  • Finally, No one answered here and I work around and find the solution for my problem

    import 'package:flutter/material.dart';
    import 'package:fluttertoast/fluttertoast.dart';
    
    class NavDrawer extends StatefulWidget with PreferredSizeWidget {
      @override
      State<StatefulWidget> createState() {
        return new NavDrawerState();
      }
    
      @override
      // TODO: implement preferredSize
      Size get preferredSize => throw UnimplementedError();
    }
    
    class NavDrawerState extends State<NavDrawer> {
      int _selectedDrawerIndex = 0;
      String screenName = "Home";
    
      final ScrollController scroll = ScrollController();
    
      @override
      void dispose() {
        scroll.dispose();
        super.dispose();
      }
    
    
    
      _getDrawerItemWidget(String pos) {
        switch (pos) {
          case "Home":
            return new HomeScreen();
          case "Receiving":
            return new FirstScreen();
          case "Putaway":
            return new SecondScreen();
          case "Pallet Transfer":
            return new ThirdScreen();
    
          default:
            return new Text("Error");
        }
      }
    
      _onSelectItem(String screen) {
        setState(() => screenName = screen);
        Navigator.of(context).pop(); // close the drawer
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: CustomAppBar(
            // here we display the title corresponding to the fragment
            // you can instead choose to have a static title
            child: Text(screenName),
            context: context,
          ),
          drawer: Drawer(
              child: Container(
            decoration: AppConstants.customBoxDecoration, 
            //you can use your own boxdecoration
            child: Column(
              children: <Widget>[
                 UserAccountsDrawerHeader(
                    decoration: AppConstants.customBoxDecoration,
                    currentAccountPicture: new CircleAvatar(
                      radius: 60.0,
                     // backgroundColor: const Color(0xFF778899),
                      backgroundImage: AssetImage('assets/logo.png'), 
                    ),
                    accountName: new Text("Name"),
                    accountEmail: new Text("mail")),
                Flexible(
                  child: ListView.builder(
                      shrinkWrap: true,
                      controller: scroll,
                      itemCount: StringConstants.menuList.length,
                      itemBuilder: (BuildContext context, int index) => buildList(
                        StringConstants.menuList[index],
                          ))
                )
              ],
            ),
          )),
          body: _getDrawerItemWidget(screenName),
        );
      }
    
      // This function recursively creates the multi-level list rows.
      Widget _buildTiles(Entry root) {
        if (root.children.isEmpty) {
          return ListTile(
            leading: Icon(root.icon),
            title: Text(
              root.title,
              style: AppConstants.textStyleNavDrawer,
            ),
            onTap: () {
              Fluttertoast.showToast(msg: root.title);
              _onSelectItem(root.title);
            },
          );
        }
        return ExpansionTile(
          key: PageStorageKey<Entry>(root),
          maintainState: true,
          title: Text(
            root.title,
            style: AppConstants.textStyleNavDrawer,
          ),
          children: root.children.map<Widget>(_buildTiles).toList(),
        );
      }
    
      Widget buildList(Entry entry) {
        return _buildTiles(entry);
      }
    }
    
    

    Custom app bar class

    
    import 'package:flutter/material.dart';
    import 'package:fluttertoast/fluttertoast.dart';
    
    class CustomAppBar extends StatelessWidget implements PreferredSizeWidget {
      final Widget child;
      final double height;
      final BuildContext context;
    
      CustomAppBar(
          {required this.child,
          this.height = kToolbarHeight,
          required this.context});
    
      @override
      Size get preferredSize => Size.fromHeight(height);
    
      @override
      Widget build(BuildContext context) {
        return AppBar(
          title: child,
          flexibleSpace: Container(
            decoration: AppConstants.customBoxDecoration,
          ),
          actions: <Widget>[
            IconButton(
              icon: Icon(
                Icons.qr_code_scanner,
                color: Colors.white,
              ),
              onPressed: () {
    
                Fluttertoast.showToast(msg: context.toString());
                // do something
              },
            )
          ],
        );
      }
    }
    
    
    

    App constants class

    
    import 'package:flutter/material.dart';
    
    class AppConstants {
    
      static const BoxDecoration customBoxDecoration = BoxDecoration(
          gradient: LinearGradient(
              begin: Alignment.topLeft,
              end: Alignment.bottomRight,
              colors: [CustomColors.iconSplashColor, CustomColors.iconColor]));
    // you define your own colors
    
      static const TextStyle textStyleNavDrawer = TextStyle(
        color: Colors.white);
    
    
    
    }
    
    

    Try this example if you need a single app bar for different screens