androidfluttermenudrawer

Flutter custom menu integration


I tried to implement the menu drawer on the main screen to avoid redundancy, it works on the sample screens, however, if the widget structures becomes a little bit complex, the icon button menu cannot access the menu drawer on the main screen.

I can just add endDrawer: CustomDrawer(); on the complex ones but if I do that the menu overlay will only appear as an overlay on the actual screen content, disregarding the bottom navbar.

Here is the main screen code

import 'package:flutter/material.dart';
import 'package:waste_pal/scan.dart';
import 'package:waste_pal/history.dart';
import 'package:waste_pal/profile.dart';
import 'package:waste_pal/home.dart';
import 'package:waste_pal/widgets/bottom-nav.dart';
import 'package:waste_pal/widgets/menu-drawer.dart';

class MainScreen extends StatefulWidget {
  @override
  _MainScreenState createState() => _MainScreenState();
}

class _MainScreenState extends State<MainScreen> {
  int _selectedIndex = 0;

  final List<Widget> _screens = [
    HomeScreen(),
    ScanScreen(),
    HistoryScreen(),
    ProfileScreen(),
  ];

  void _onItemTapped(int index) {
    setState(() {
      _selectedIndex = index;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      endDrawer: CustomDrawer(),
      body: IndexedStack(
        index: _selectedIndex,
        children: _screens,
      ),
      bottomNavigationBar: CustomNavBar(
        selectedIndex: _selectedIndex,
        onItemTapped: _onItemTapped,
      ),
    );
  }
}

The menu drawer

import 'package:flutter/material.dart';
import 'package:waste_pal/main.dart';

class CustomDrawer extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Drawer(
      width: MediaQuery.of(context).size.width * 0.6, 
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.only(
          topLeft: Radius.circular(30),
          bottomLeft: Radius.circular(30),
        ),
      ),
      backgroundColor: Color(0xFFBFD9C5),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          // Close Button
          Padding(
            padding: const EdgeInsets.fromLTRB(20, 40, 20, 10),
            child: Align(
              alignment: Alignment.topLeft,
              child: IconButton(
                icon: Icon(Icons.close, size: 30, color: Colors.black),
                onPressed: () => Navigator.of(context).pop(),
              ),
            ),
          ),

          SizedBox(height: 30),

          // Menu Items
          Expanded(
            child: ListView(
              padding: EdgeInsets.zero,
              children: [
                _buildDrawerItem(Icons.home, "Home", context),
                _buildDrawerItem(Icons.card_giftcard, "Redeem", context),
                _buildDrawerItem(Icons.quiz, "Quizlet", context),
                _buildDrawerItem(Icons.library_books, "WasteKnows", context),
                _buildDrawerItem(Icons.settings, "Settings", context),
                _buildDrawerItem(Icons.help_outline, "Helps & FAQs", context),
              ],
            ),
          ),

          // Log Out Button at the Bottom
          Padding(
            padding: const EdgeInsets.all(20.0),
            child: ElevatedButton(
              style: ElevatedButton.styleFrom(
                backgroundColor: Color(0xFF2D694F),
                shape: RoundedRectangleBorder(
                  borderRadius: BorderRadius.circular(30),
                ),
                minimumSize: Size(double.infinity, 50),
              ),
              onPressed: () {
                 Navigator.push(
                            context,
                            MaterialPageRoute(
                                builder: (context) => LoginPage()),
                          );
              },
              child: Text(
                "Log out",
                style: TextStyle(color: Colors.white, fontSize: 16),
              ),
            ),
          ),

          SizedBox(height: 20),
        ],
      ),
    );
  }

  // Helper method for creating menu items
  Widget _buildDrawerItem(IconData icon, String title, BuildContext context) {
  return Padding(
    padding: const EdgeInsets.symmetric(vertical: 15.0), // Adjust spacing
    child: GestureDetector(
      onTap: () => Navigator.pop(context),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.center, // Center both icon and text
        children: [
          Icon(icon, color: Color.fromARGB(255, 9, 93, 64)), // Centered Icon
          SizedBox(width: 10), // Space between icon and text
          Text(
            title,
            textAlign: TextAlign.center,
            style: TextStyle(fontSize: 18, fontWeight: FontWeight.w500),
          ),
        ],
      ),
    ),
  );
}
}

This is the sample screen where the menu works


class HistoryScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("History"),
        backgroundColor: Color(0xFF0E5B3B),
        leading: IconButton(
          icon: Icon(Icons.menu),
          onPressed: () {
            Scaffold.of(context).openEndDrawer(); // Open drawer
          },
        ),
      ),
      body: Center(child: Text('History Screen', style: TextStyle(fontSize: 24))),
    );
  }
}

Now this is the complex one

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

class ScanScreen extends StatefulWidget {
  @override
  _ScanScreenState createState() => _ScanScreenState();
}

class _ScanScreenState extends State<ScanScreen> {
  CameraController? _controller;
  List<CameraDescription>? cameras;
  bool _isCameraInitialized = false;

  @override
  void initState() {
    super.initState();
    _initCamera();
  }

  Future<void> _initCamera() async {
    cameras = await availableCameras();
    if (cameras != null && cameras!.isNotEmpty) {
      _controller = CameraController(cameras![0], ResolutionPreset.medium);
      await _controller!.initialize();
      if (!mounted) return;
      setState(() {
        _isCameraInitialized = true;
      });
    }
  }

  @override
  void dispose() {
    _controller?.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: PreferredSize(
        preferredSize: Size.fromHeight(70),
        child: AppBar(
          elevation: 0,
          centerTitle: false,
          automaticallyImplyLeading: false,
          title: Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              Image.asset('assets/logo.png',
                  height: 70), 
              Text(
                'WASTE PAL',
                style: TextStyle(
                  fontSize: 30,
                  fontFamily: 'Montserrat',
                  fontWeight: FontWeight.w900,
                  color: const Color.fromARGB(255, 35, 62, 46),
                ),
              ),
              Builder(
                builder: (context) => IconButton(
                  icon: Icon(Icons.menu,
                      size: 40, color: Color.fromARGB(255, 35, 62, 46)),
                  onPressed: () {
                    Scaffold.of(context).openEndDrawer();
                  },
                ),
              ),
            ],
          ),
        ),
      ),
      
      body: Column(
        children: [
          Padding(
            padding: EdgeInsets.all(10),
            child: Container(
              padding: EdgeInsets.symmetric(vertical: 20),
              width: double.infinity,
              decoration: BoxDecoration(
                color: Colors.green[800],
                borderRadius: BorderRadius.circular(20),
              ),
              child: Center(
                child: Text(
                  'Take a photo of the item',
                  style: TextStyle(
                    fontSize: 20,
                    fontWeight: FontWeight.bold,
                    fontFamily: 'Montserrat',
                    color: Colors.white,
                  ),
                ),
              ),
            ),
          ),
          Expanded(
            child: _isCameraInitialized
                ? ClipRRect(
                    borderRadius: BorderRadius.circular(20),
                    child: CameraPreview(_controller!),
                  )
                : Center(child: CircularProgressIndicator()),
          ),
          SizedBox(height: 10),
          ElevatedButton(
            onPressed: () async {
              try {
                final XFile image = await _controller!.takePicture();
                print("Picture taken: ${image.path}");
                // You can now use the image path for further processing
              } catch (e) {
                print("Error capturing image: $e");
              }
            },
            style: ElevatedButton.styleFrom(
              shape: CircleBorder(),
              padding: EdgeInsets.all(20),
              backgroundColor: Colors.green[700],
            ),
            child: Icon(Icons.camera_alt, color: Colors.white, size: 30),
          ),
          SizedBox(height: 20)
        ],
      ),
    );
  }
}

Now this is what it looks like on complex screens with implementation of endDrawer: CustomDrawer();This is the goal, this is what it looks like on the sample screens


Solution

  • Till I understood your question you are trying to achieve this kind of ui.

    enter image description here

    This Link can help you to achieve the ui, there are various kinds of GUI available which can help you. I have added code which can also help to achieve the UI, hope it will work.

    Plugin :
      awesome_drawer_bar: '<latest_release>'
    
    AwesomeDrawerBar(
          controller: AwesomeDrawerBarController,
          menuScreen: MENU_SCREEN,
          mainScreen: MAIN_SCREEN,
          borderRadius: 24.0,
          showShadow: true,
          angle: -12.0,
          backgroundColor: Colors.grey[300],
          slideWidth: MediaQuery.of(context).size.width*.65,
          openCurve: Curves.fastOutSlowIn,
          closeCurve: Curves.bounceIn,
        )