Olá devs!
Hoje vou mostrar como criar um Navigation Drawer com efeitos bem diferenciados usando o Widget Stack. Vamos começar?

O Widget Stack serve para empilhar vários widgets sobrepondo um sobre os outros, com isso poderemos utilizar animações para fazer um dos widgets revelar o menu lateral, fazendo assim o efeito “Menu de Gaveta”. Acompanhe o Resultado final a baixo:

INICIANDO

Primeiro vamos iniciar um novo projeto com o VSCode ou Android Studio. Usaremos apenas códigos nativos e não necessitaremos de nenhuma API externa para esse processo. Removendo todos os códigos desnecessário nós teremos no main.dart o método principal main() e o widget inicial MyApp. 

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      theme: new ThemeData(
        primarySwatch: Colors.blueGrey,
      ),
      home: new Start(),
    );
  }
}

Temos agora que construir o nosso Widget padrão que será o body do MaterialApp. Chamei esse Widget de Start, mas notem que se trata de um StatefulWidget, ou sejá, posteriomente estaremos alterando seu estado para trabalhar com as animações. O nosso arquivo start.dart ficará assim:

import 'package:flutter/material.dart';


class Start extends StatefulWidget {

  @override
  _StartState createState() => new _StartState();
}

class _StartState extends State<Start> {
 

  @override
  Widget build(BuildContext context) {
    return Stack(
        children: <Widget>[
            DrawerWidget(),//widget do drawer
            ScreenWidget()//tela inicial
        ],
      );
  }
}

Eu prefiro dividir todos os widgets em arquivos para facilitar o processo de uma futura manutenção e também deixar mais ditádico para esse tutorial.
No Start nos criamos o Stack que irá empilhar os 2 widget para essa demonstração de Drawer.
Agora vamos criar o DrawerWidget e o ScreenWidget

DRAWER

O DrawerWidget será a primeira camada do Stack que mostrar o menu lateral. Vamos criar no arquivo drawer.dart o Widget DrawerWidget.

import 'package:flutter/material.dart';

class DrawerWidget extends StatefulWidget {
  @override
  _DrawerWidgetState createState() => new _DrawerWidgetState();
}

class _DrawerWidgetState extends State<DrawerWidget> {


 @override
  Widget build(BuildContext context) {
    return Material(
          child: Container(
              width: double.infinity,
              height: double.infinity,
              color: Colors.amber),
    );
  }
}

Isso nos dará uma tela Amarelada no nosso emulador. 
Agora precisamos criar um ListView com ListTiles pra representar nosso Menu. Essa é uma implementação muito simples, dividi em Métodos para ficar mais didático.

import 'package:flutter/material.dart';

class DrawerWidget extends StatefulWidget {
  @override
  _DrawerWidgetState createState() => new _DrawerWidgetState();
}

class _DrawerWidgetState extends State<DrawerWidget> {

//identifica qual item foi selecionado
 int itemSelect = 0;

//Cria uma listview com os itens do menu
Widget _listMenu() {
    return ListView(
      children: <Widget>[
        _tiles("INÍCIO", Icons.home, 0, () {}),
        _tiles("NOVIDADES", Icons.home, 1, () {}),
        _tiles("PROMOÇÕES", Icons.home, 2, () {}),
        Divider(),
        _tiles("SAIR", Icons.home, 3, () {}),
       
      ],
    );
  }

//cria cada item do menu
  Widget _tiles(String text, IconData icon, int item, Function onTap) {
    return ListTile(
      leading: Icon(icon),
      onTap: onTap,
      selected: item == itemSelect,
          title: Text(
            text,
            style: TextStyle(fontWeight: FontWeight.bold),
          ),
        );
  }

  @override
  Widget build(BuildContext context) {
    return Material(
          child: Container(
              width: double.infinity,
              height: double.infinity,
              color: Colors.amber,
              child: _listMenu()),
    );
  }
}

Com isso temos esse resultado:

Agora usaremos o CircleAvatar para finalizar o Drawer e colocaremos informações como nome email. Para isso criaremos mais um método local.

 //cria o avatar com nome, email e imagem
  Widget _avatar() {
    return Padding(
      padding: EdgeInsets.all(18.0),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: <Widget>[
          CircleAvatar(
            backgroundColor: Colors.brown.shade800,
            child: Text('JM'),
          ),
          Container(height: 12.0,),
          Text("Jacob Moura", style: TextStyle(fontSize: 18.0, fontWeight: FontWeight.bold, color: Colors.black54),),
          Text("jacobaraujo7@gmail.com"),
        ],
      ),
    );
  }

Ai basta adicionar na listView

//Cria uma listview com os itens do menu
  Widget _listMenu() {
    return ListView(
      children: <Widget>[
        _avatar(), // adiciona o avatar
        _tiles("INÍCIO", Icons.home, 0, () {}),
        _tiles("NOVIDADES", Icons.add_location, 1, () {}),
        _tiles("PROMOÇÕES", Icons.grade, 2, () {}),
        Divider(),
        _tiles("SAIR", Icons.arrow_back, 3, () {}),
      ],
    );
  }

Com isso finalizamos o DrawerWidget, agora passaremos para o ScreenWidget

ScreenWidget

A ScreenWidget basicamente comportará a tela inicial, então aqui criaremos um Scafold comum para visualizamos a Toolbar e adicionaremo o botão para chamar o Drawer. Vamos programar o screen.dart;

 import 'package:flutter/material.dart';

class ScreenWidget extends StatefulWidget {

  final Function onTap;

  const ScreenWidget({Key key, this.onTap}) : super(key: key);

  @override
  _DrawerWidgetState createState() => new _DrawerWidgetState();
}

class _DrawerWidgetState extends State<ScreenWidget> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Custom Drawer"),
        leading: GestureDetector(
          onTap: widget.onTap,
          child: Icon(Icons.menu)),
      ),
      body: Center(
        child: Text("Tela Inicial"),
      ),
    );
  }
}

Note que após códificarmos o ScreenWidget ele sobrepõem o DrawerWidget graças ao Stack, porém o Drawer existe atrás do mesmo.

A única peculiaridade que vimos foi no ScreenWidget que recebe no Construtor uma Function que é repassada ao ícone de menu, assim poderemos chamar o Drawer mesmo ele não estando construido nesse Widget.

ANIMAÇÕES

Agora finalmente chegamos no processo mais divertido do Drawer, agora iremos trabalhar no arquivo start.dart e movimentar o ScreenWidget para que torne o DrawerWidget visível.

Começaremos criando um AnimationController pra disparar nossas animações quando o usuário clickar no botão de menu.

class _StartState extends State<Start> with SingleTickerProviderStateMixin {
 
  AnimationController controller;

  @override
  void initState() {
    super.initState();
    controller =
        AnimationController(duration: Duration(milliseconds: 800), vsync: this);
  }

  @override
  void dispose() {
    controller.dispose();
    super.dispose();
  }

Fizemos algumas modificações importantes aqui: Primeiro adicionamos o “with SingleTickerProviderStateMixin” para sincronizar o ticket da animação com o controller, e sobreescrevemos 2 métodos, o “initState()” e o “dispose()”, se são métodos chamados no início e no final do cíclo de vida do Widget. Nesses métodos eu instanciei o AnimationController e como boa prática, encerrei a instância do mesmo.
Continuando, iremos criar alguns Animations pra mudar o estado e transladar o ScreenWidget. Primario vamos criar o Animation para mover o widget.

(NOTA: Apartir desse momento você precisará reiniciar o aplicativo no emulador pra que possa funcionar);

class _StartState extends State<Start> with SingleTickerProviderStateMixin {
 
  AnimationController controller;
  Animation animationTranslate;


  @override
  void initState() {
    super.initState();
    controller =
        AnimationController(duration: Duration(milliseconds: 800), vsync: this);
animationTranslate = Tween(begin: 0.0, end: 1.0)
        .animate(CurvedAnimation(parent: controller, curve: Curves.easeOut));
    animationTranslate.addListener(() {
      setState(() {});
    });
  }

Estamos utilizando o Tween para criar uma Animation com valores de começo e fim, com isso podemos trabalhar entre esses dois estados durante a animação. Iremos agora fazer o ScreenWidget se mover. Primeiro vamos Colocalo dentro de um Container e trabalhar com a propriedade transform.

@override
  Widget build(BuildContext context) {
    return Stack(
      children: <Widget>[
        DrawerWidget(), //widget do drawer
        Container(
          transform: Matrix4.identity()..translate(animationTranslate.value, 0.0),
          child: ScreenWidget()) //tela inicial
      ],
    );
  }
}

Note que o “translate” recebe 2 valores, o x e o y, como queremos mover para a horizontal iremos trabalhar com o eixo X. Entao adicionamos o animationTranslate.value que nos dar um valor dinâmico no eixo x para fazer a animação quando o controller for chamado.
Iremos criar um método para mudar o estado da animação passando uma função para o construtor do ScreenWidget.

_onTapMenu(){

    if(controller.value == 1){
        controller.reverse();
    } else {
        controller.forward();
    }

  }

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: <Widget>[
        DrawerWidget(), //widget do drawer
        Container(
          transform: Matrix4.identity()..translate(animationTranslate.value, 0.0),
          child: ScreenWidget(onTap: _onTapMenu,)) //tela inicial
      ],
    );
  }

O AnimationController representado pela variável controller também tem um value. Quando a animação não foi iniciada o valor é zero e quando foi completada o valor é 1, com isso podemos colocar um condição para fazer a animação executar ou retroceder baseada nesses valores. Agora você já pode reiniciar sua aplicação e testar.

Pra finalizar, criaremos mais 2 Animation, uma para o diminuir e aumentar o tamanho, e a outra para a borda.
Usaremos o ClipRRect para fazer uma borda circular e Transform.scale para diminuir e aumentar o ScreenWidget.

import 'package:custom_drawer/drawer.dart';
import 'package:custom_drawer/screen.dart';
import 'package:flutter/material.dart';

class Start extends StatefulWidget {
  @override
  _StartState createState() => new _StartState();
}

class _StartState extends State<Start> with SingleTickerProviderStateMixin {
  AnimationController controller;

  Animation animationTranslate;
  Animation animationSize;
  Animation animationSizeBorder;

  @override
  void initState() {
    super.initState();
    controller =
        AnimationController(duration: Duration(milliseconds: 800), vsync: this);

    animationTranslate = Tween(begin: 0.0, end: 300.0)
        .animate(CurvedAnimation(parent: controller, curve: Curves.easeOut));
    animationTranslate.addListener(() {
      setState(() {});
    });

    animationSize = Tween(begin: 1.0, end: 0.8)
        .animate(CurvedAnimation(parent: controller, curve: Curves.easeOut));
    animationSize.addListener(() {
      setState(() {});
    });

    animationSizeBorder = Tween(begin: 0.0, end: 10.0)
        .animate(CurvedAnimation(parent: controller, curve: Curves.easeOut));
    animationSizeBorder.addListener(() {
      setState(() {});
    });
  }

  @override
  void dispose() {
    controller.dispose();
    super.dispose();
  }

  _onTapMenu() {
    if (controller.value == 1) {
      controller.reverse();
    } else {
      controller.forward();
    }
  }

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: <Widget>[
        DrawerWidget(), //widget do drawer
        Transform.scale(
          scale: animationSize.value,
          child: Container(
              transform: Matrix4.identity()
                ..translate(animationTranslate.value, 0.0),
              child: ClipRRect(
                  borderRadius:
                      BorderRadius.circular(animationSizeBorder.value),
                  child: ScreenWidget(
                    onTap: _onTapMenu,
                  ))),
        ) //tela inicial
      ],
    );
  }
}

E por fim, chegamos ao resultado esperado.

Você também pode colocar uma sombra no Container que também daria um efeito interessante.
Esse post foi para mostrar como é fácil criar protótipos incríveis sem a necessidade de uma lib externa. O Flutter é Maravilhoso!

Todo o código mostrado aqui está no Microsoft GitHub (LOL) para que você possa testar no seu Ambiente.

Abraços!