Flutter - Material Design com Scaffold


 Hoje veremos como usar o widget Scaffold do Material Design no Flutter.

O que é o Flutter ?

O Flutter é um SDK de aplicativo móvel do Google que ajuda a criar aplicativos móveis modernos para iOS e Android usando uma única (quase) base de código.

Ao contrário de outros frameworks como o React Native, ele não usa JavaScript como uma linguagem de programação nem precisa de uma ponte de interpretação para converter código JavaScript em código nativo, em vez disso, ele utiliza a linguagem Dart e compila diretamente em binários arms e é executado na plataforma nativa.

Se você não conhece o Flutter veja o meu artigo :  Flutter - Primeiros contatos e impressões

Material Design

Antes de entrar no assunto do artigo, o widget Scaffold, precisamos apresentar o Material Design.

"Material Design" é uma "linguagem de design" desenvolvida pelo Google. Essa nova metodologia de design foi criada em 2014 e hoje é uma das maiores tendências no design.

O termo Material Design tem a ver com a criação de uma experiência de usuário onipresente e intuitiva. Para detalhes veja o site: https://material.io/design

Alguns dos principais recursos que um aplicativo com tema Material Desing deve ter são:

Scaffold

No Flutter, o widget Scaffold implementa a estrutura do layout visual do Material Design básico e permite definir outros widgets do Material Design no seu interior.

O widget Scaffold é bom o suficiente para criar um aplicativo móvel de uso geral, e, contém quase tudo que você precisa para criar um aplicativo funcional e responsivo.

Em geral usamos o MaterialApp uma vez na aplicação, e, o widget Scaffold para cada tela que desejamos desenhar.

Este widget possui diversos parâmetros em seu construtor. Neste artigo veremos os seguintes parãmetros :

Podemos ver o uso de dois parâmetros mínimos, os widgets AppBar e Body, em um trecho de código padrão :

mport 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primaryColor : Colors.blue
      ),
       home :  Scaffold (
        appBar: AppBar(
          title: Text("Ola Flutter"),
        ),
        body: Container(
          color: Colors.orange,
        )
      )
    );
  }
}

Abaixo temos a figura que exibe a hierarquia do widget Scaffold :

A seguir vejamos cada um deles.

1- AppBar e Body

Nenhuma aplicação Scaffold está completa sem usar AppBar e Body, pois são os widgets mínimos que precisam ser usados ​​para criar um Design de Material Design.

Enquanto parâmetro body pode usar qualquer widget, o parâmetro appbar só pode usar o widget AppBar como entrada. Embora o próprio AppBar utilize vários argumentos, é possível criar um AppBar vazio para fins de visualização.

Aqui está como podemos usar Scaffold com appbar e body:

import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(),
        body:Text("Flutter - Scaffold"))
    );
  }
}

Embora o widget AppBar ofereça muitas funcionalidades interessantes, no entanto, aqui forneceremos um título simples para o AppBar usando o parâmetro title do construtor:

import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text("Scalffold App"), 
        ),
        body:Text("Flutter - Scaffold"))
    );
  }
}

Podemos aplicar várias permutações e combinações à exibição do AppBar, como alterar a cor do plano de fundo para laranja e usar ícones (ícone home) em vez de texto como título.

import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
           title : Icon(Icons.home),
           backgroundColor: Colors.orange  
        ),
        body:Text("Flutter - Scaffold"))
    );
  }
}

O body pode ter qualquer widget de propósito geral e, a escolha vai depender das informações que queremos exibir em nosso aplicativo.

2- Float Action Button

Um Float action Button ou botão de ação flutuante, é um botão de ícone circular que é exibido o tempo todo e geralmente serve para promover uma ação primária ou mais amplamente usada na tela.

Este botão é criado usando o widget FloatingActionButton() com um mínimo de dois parâmetros de construtor chamados ‘child:’ e ‘onPressed:’

O "child:" é usado para adicionar um texto : "Clique", enquanto "onPressed:"  é chamado toda vez que o usuário pressiona o botão, sendo usado para acionar a ação desejada.

Para simplificar, vamos apenas imprimir uma mensagem na janela do console quando o usuário pressionar o botão.

import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
       home: Scaffold(
        appBar: AppBar(
           title : Icon(Icons.home),
           backgroundColor: Colors.orange  
        ),
        body:Text("Flutter - Scaffold"),
        floatingActionButton: FloatingActionButton(
          child: Text("Clique"),
          onPressed: () {
            print("O botão foi pressionado");
          }
        ),
        )
    );
  }
}

Na figura a seguir temos o texto sendo exibido no console ao clicar o botão:

Embora possamos exibir textos no botão, é uma prática geral usar ícones/imagens, por dois motivos:

  1. Ícones ou imagens são mais propensos a acionar a ação do usuário;
  2. É difícil encaixar texto longo e significativo dentro do pequeno espaço do botão;

Por exemplo, para adicionar um ícone, podemos simplesmente alterar "child:" assim:

import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
       home: Scaffold(
        appBar: AppBar(
           title : Icon(Icons.home),
           backgroundColor: Colors.orange  
        ),
        body:Text("Flutter - Scaffold"),
        floatingActionButton: FloatingActionButton(
          child: Icon(Icons.Add_a_photo),
          onPressed: () {
            print("O botão foi pressionado");
          }
        ),
        )
    );
  }
}

3- Bottom Navigation Bar

Como o nome sugere, a barra de navegação inferior, assim como o AppBar, é uma faixa horizontal na parte inferior da tela. Pode ter vários itens e pode usar rótulos de texto, ícones ou uma combinação de ambos.

A barra de navegação inferior geralmente é criada para exibir mensagens, bem como para fornecer ações de atalho específicas da página.

Podemos criar um Bottom Navigation bar a partir do widget  "BottomNavigationBar", no entanto, com "Scaffold" e "FloatingActionButton", usamos o widget "BottomAppBar" porque ele fornece o espaço para o botão de ação flutuante e não o sobrepõe.

Veja como podemos criar uma barra de navegação inferior com "BottomAppBar":

import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home: Scaffold(
      appBar: AppBar(title: Icon(Icons.home), 
             backgroundColor: Colors.orange),
      body: Text("Flutter - Scaffold"),
      floatingActionButton: FloatingActionButton(
          child: Icon(Icons.add_a_photo),
          onPressed: () {
            print("O botão foi pressionado");
          }),
          bottomNavigationBar: BottomAppBar(
            child: Text("Meu bottomNavigationBar"),
              color: Colors.lime,
      ),
    ));
  }

Como você pode ver na figura acima, ela não se sobrepõe ao botão de ação flutuante.

Para exibir vários widgets dentro de "BottomAppBar()", precisamos usar um contêner de widgets, como "Row()", que podem encapsular vários widgets filhos dentro dele. Veja como fica:

import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home: Scaffold(
      appBar: AppBar(title: Icon(Icons.home),
 backgroundColor: Colors.orange),
      body: Text("Flutter - Scaffold"),
      floatingActionButton: FloatingActionButton(
          child: Icon(Icons.add_a_photo),
          onPressed: () {
            print("O botão foi pressionado");
          }),
      bottomNavigationBar: BottomAppBar(
        child: Row(
          children: <Widget>[
            Text("bottomNavigationBar 1  "),
            Text("bottomNavigationBar 2"),
          ],
        ),
        color: Colors.lime,
      ),
    ));
  }
}

Podemos combinar texto e ícones onde os ícones podem ser apenas de exibição estática ou um botão com ações associadas.

Vamos ver um exemplo de como podemos criar os dois: texto e ícone. Novamente, para simplificar, estamos apenas imprimindo uma declaração na janela do console quando o botão do ícone for pressionado:

import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home: Scaffold(
      appBar: AppBar(title: Icon(Icons.home), 
backgroundColor: Colors.orange),
      body: Text("Flutter - Scaffold"),
      floatingActionButton: FloatingActionButton(
          child: Icon(Icons.add_a_photo),
          onPressed: () {
            print("O botão foi pressionado");
          }),
      bottomNavigationBar: BottomAppBar(
        child: Row(
          children: <Widget>[
            Text("bottomNavigationBar 1  "),
            Icon(Icons.home),
            IconButton(
              icon: Icon(Icons.add_alert),
              onPressed: () {
                print("O IconButton foi pressionado");
              }
              ,)
          ],
        ),
        color: Colors.lime,
      ),
    ));
  }
}

Por padrão, a barra de navegação inferior ocupa uma quantidade mínima de espaço na parte inferior da tela. No entanto, se seu aplicativo precisar, você pode aumentar o tamanho da barra de navegação inferior agrupando o child no widget 'Container()' e especificando o parâmetro height como é mostrado a seguir:

import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home: Scaffold(
      appBar: AppBar(title: Icon(Icons.home), 
backgroundColor: Colors.orange),
      body: Text("Flutter - Scaffold"),
      floatingActionButton: FloatingActionButton(
          child: Icon(Icons.add_a_photo),
          onPressed: () {
            print("O botão foi pressionado");
          }),
      bottomNavigationBar: BottomAppBar(
        child: Container(
          height: 100.0,
        child: Row(
          children: <Widget>[
            Text("bottomNavigationBar 1  "),
            Icon(Icons.home),
            IconButton(
              icon: Icon(Icons.add_alert),
              onPressed: () {
                print("O IconButton foi pressionado");
              },
            )
          ],
        ),
        ),
        color: Colors.lime,
      ),
    ));
  }
}
 

Note que mesmo aumentando o tamanho do bottom navigation bar ele não sobrepõe o floating action bar.

4- Persistente Footer Buttons

O Persistente Footer Button (Botão de rodapé persistente) exibe um conjnto de botões que permanecem persistentes na tela, mesmo que o corpo do Scaffold role.

Se usada junto com a barra de navegação inferior, ela é exibida acima dela, mas abaixo do body.

Também podemos usar os botões planos(Flat Buttons) aqui, mas, para simplificar, usaremos o widget "IconButton()" sem a função "onPressed:".

import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home: Scaffold(
      appBar: AppBar(title: Icon(Icons.home), 
backgroundColor: Colors.orange),
      body: Text("Flutter - Scaffold"),
      floatingActionButton: FloatingActionButton(
          child: Icon(Icons.add_a_photo),
          onPressed: () {
            print("O botão foi pressionado");
          }),
      bottomNavigationBar: BottomAppBar(
        child: Container(
          height: 100.0,
          child: Row(
            children: <Widget>[
              Text("bottomNavigationBar 1  "),
              Icon(Icons.home),
              IconButton(
                icon: Icon(Icons.add_alert),
                onPressed: () {
                  print("O IconButton foi pressionado");
                },
              )
            ],
          ),
        ),
        color: Colors.lime,
      ),
     persistentFooterButtons: <Widget>[
     IconButton (icon: Icon(Icons.access_time),onPressed: null,),
     IconButton (icon: Icon(Icons.account_balance),onPressed: null,),
     ],
    ));
  }
}

Você já percebeu que o Botão de rodapé persistente  usa um array de widgets e não tem propriedades associadas a eles, como background, color, height, etc.

Ele compartilha a cor de fundo com a cor de fundo do body Scaffold. Então se eu mudar a cor de fundo do Scaffold isso vai se refletir no Botão de rodapé. Veja:

import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home: Scaffold(
      appBar: AppBar(title: Icon(Icons.home), 
backgroundColor: Colors.orange),
      body: Text("Flutter - Scaffold"),
      floatingActionButton: FloatingActionButton(
          child: Icon(Icons.add_a_photo),
          onPressed: () {
            print("O botão foi pressionado");
          }),
      bottomNavigationBar: BottomAppBar(
        child: Container(
          height: 100.0,
          child: Row(
            children: <Widget>[
              Text("bottomNavigationBar 1  "),
              Icon(Icons.home),
              IconButton(
                icon: Icon(Icons.add_alert),
                onPressed: () {
                  print("O IconButton foi pressionado");
                },
              )
            ],
          ),
        ),
        color: Colors.lime,
      ),
      persistentFooterButtons: <Widget>[
        IconButton (icon: Icon(Icons.access_time),onPressed: null,),
        IconButton (icon: Icon(Icons.account_balance),
onPressed: null,),
      ],
     backgroundColor : Colors.pink,
    ));
  }
}

4- Drawer - Navigation drawer

Para concluir vou mostrar a opção drawer do Scaffold que permite criar um navigation drawer.

No exemplo abaixo estou usando a opção drawer e incluindo um Contâiner com a cor red:

import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primaryColor : Colors.blue
      ),
      home :  Scaffold(
        appBar: AppBar(
          title: Text("Ola Flutter"),
        ),
        body : Container (
          color: Colors.orange,
        ),
        drawer: Container(
          color: Colors.red,
        )
      )
    );
  }
}

Para criar um menu navigation com opções basta definir um Drawer e seu filho como um ListView com uma lista de filhos ListTile onde definimos o texto e o ícone:

import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        debugShowCheckedModeBanner: false,
        theme: ThemeData(primaryColor: Colors.blue),
        home: Scaffold(
          appBar: AppBar(
            title: Text("Ola Flutter"),
          ),
          body: Container(
            color: Colors.orange,
          ),
          drawer: Drawer(
            child: ListView(
              children: <Widget>[
                ListTile(
                  title: Text("Item 1"),
                  trailing: Icon(Icons.arrow_forward),
                ),
                ListTile(
                  title: Text("Item 2"),
                  trailing: Icon(Icons.arrow_forward),
                ),
              ],
            ),
          ),
    ));
  }
}

E temos assim um resumo da utilização do widget Scaffold e dos seus principais parâmetros.

Na próxima parte do artigo veremos como criar uma aplicação simples usando os recurso do Scaffold.

"Grandes são as obras do Senhor, procuradas por todos os que nelas tomam prazer."
Salmos 111:2

Veja os Destaques e novidades do SUPER DVD Visual Basic (sempre atualizado) : clique e confira !

Quer migrar para o VB .NET ?

Quer aprender C# ??

Quer aprender os conceitos da Programação Orientada a objetos ?

Quer aprender o gerar relatórios com o ReportViewer no VS 2013 ?

Referências:


José Carlos Macoratti