Desenvolvimento Web e Desktop Responsivo com Flutter
Publicados: 2022-03-10Este tutorial não é uma introdução ao próprio Flutter. Existem muitos artigos, vídeos e vários livros disponíveis on-line com introduções simples que ajudarão você a aprender o básico do Flutter. Em vez disso, abordaremos os dois objetivos a seguir:
- O estado atual do desenvolvimento não móvel do Flutter e como você pode executar o código Flutter no navegador, em um computador desktop ou laptop;
- Como criar aplicativos responsivos usando o Flutter, para que você possa ver seu poder - especialmente como uma estrutura da Web - em exibição completa, terminando com uma nota sobre roteamento baseado em URL.
Vamos entrar nisso!
O que é Flutter, por que é importante, no que evoluiu, para onde está indo
Flutter é a mais recente estrutura de desenvolvimento de aplicativos do Google. O Google prevê que seja abrangente: permitirá que o mesmo código seja executado em smartphones de todas as marcas, em tablets e em computadores desktop e laptops como aplicativos nativos ou como páginas da web.
É um projeto muito ambicioso, mas o Google tem sido incrivelmente bem-sucedido até agora, particularmente em dois aspectos: na criação de uma estrutura verdadeiramente independente de plataforma para aplicativos nativos Android e iOS que funciona muito bem e está totalmente pronta para uso em produção e na criação de uma frente impressionante -end framework web que pode compartilhar 100% do código com um aplicativo Flutter compatível.
Na próxima seção, veremos o que torna o aplicativo compatível e qual é o estado do desenvolvimento do Flutter não móvel a partir de agora.
Desenvolvimento não móvel com Flutter
O desenvolvimento não móvel com o Flutter foi divulgado pela primeira vez de maneira significativa no Google I/O 2019. Esta seção é sobre como fazê-lo funcionar e sobre quando funciona.
Como habilitar o desenvolvimento da Web e da área de trabalho
Para habilitar o desenvolvimento web, primeiro você deve estar no canal beta do Flutter. Há duas maneiras de chegar a esse ponto:
- Instale o Flutter diretamente no canal beta baixando a versão beta mais recente apropriada do arquivo SDK.
- Se você já tem o Flutter instalado, mude para o canal beta com
$ flutter channel beta
, e então execute a troca atualizando sua versão do Flutter (que na verdade é umgit pull
na pasta de instalação do Flutter) com$ flutter upgrade
.
Depois disso, você pode executar isso:
$ flutter config --enable-web
O suporte de desktop é muito mais experimental, especialmente devido à falta de ferramentas para Linux e Windows, tornando o desenvolvimento de plugins especialmente uma grande dor, e devido ao fato de que as APIs usadas para isso são destinadas ao uso de prova de conceito e não para Produção. Isso é diferente do desenvolvimento da Web, que usa o compilador dart2js testado e comprovado para compilações de lançamento, que nem são compatíveis com aplicativos de desktop nativos do Windows e Linux.
Nota : O suporte para macOS é um pouco melhor do que o suporte para Windows e Linux, mas ainda não é tão bom quanto o suporte para a Web e não é tão bom quanto o suporte completo para plataformas móveis.
Para habilitar o suporte para desenvolvimento de desktop, você precisa alternar para o canal de lançamento master
seguindo as mesmas etapas descritas anteriormente para o canal beta
. Em seguida, execute o seguinte substituindo <OS_NAME>
por linux
, windows
ou macos
:
$ flutter config --enable-<OS_NAME>-desktop
Neste ponto, se você tiver problemas com qualquer uma das etapas a seguir que descreverei porque a ferramenta Flutter não está fazendo o que estou dizendo que deveria fazer, algumas etapas comuns de solução de problemas são estas:
- Execute o
flutter doctor
para verificar se há problemas. Um efeito colateral deste comando Flutter é que ele deve baixar todas as ferramentas necessárias que não possui. - Execute
flutter upgrade
. - Desligue e ligue novamente. A antiga resposta de suporte técnico de nível 1 de reiniciar o computador pode ser exatamente o que é necessário para que você possa aproveitar todas as riquezas do Flutter.
Executando e criando aplicativos da Web Flutter
O suporte à web do Flutter não é ruim, e isso se reflete na facilidade de desenvolvimento para a web.
Executando isso…
$ flutter devices
… deve mostrar imediatamente uma entrada para algo assim:
Web Server • web-server • web-javascript • Flutter Tools
Além disso, a execução do navegador Chrome deve fazer com que o Flutter também mostre uma entrada para ele. A execução do flutter run
em um projeto Flutter compatível (mais sobre isso posteriormente) quando o único “dispositivo conectado” que aparece é o servidor web fará com que o Flutter inicie um servidor web em localhost:<RANDOM_PORT>
, que permitirá que você acesse seu Flutter aplicativo da web de qualquer navegador.
Se você instalou o Chrome, mas não está aparecendo, você precisa definir a variável de ambiente CHROME_EXECUTABLE
para o caminho para o arquivo executável do Chrome.
Executando e construindo aplicativos de desktop Flutter
Depois de habilitar o suporte para desktop Flutter, você pode executar um aplicativo Flutter nativamente em sua estação de trabalho de desenvolvimento com flutter run -d <OS_NAME>
, substituindo <OS_NAME>
pelo mesmo valor usado ao habilitar o suporte para desktop. Você também pode compilar binários no diretório de build
com flutter build <OS_NAME>
.
Antes que você possa fazer isso, você precisa ter um diretório contendo o que o Flutter precisa construir para sua plataforma. Isso será criado automaticamente quando você criar um novo projeto, mas você precisará criá-lo para um projeto existente com flutter create .
. Além disso, as APIs do Linux e do Windows são instáveis, portanto, talvez seja necessário regenerá-las para essas plataformas se o aplicativo parar de funcionar após uma atualização do Flutter.
Quando um aplicativo é compatível?
O que eu quis dizer o tempo todo ao mencionar que um aplicativo Flutter precisa ser um “projeto compatível” para funcionar na área de trabalho ou na web? Simplificando, quero dizer que ele não deve usar nenhum plug-in que não tenha uma implementação específica de plataforma para a plataforma na qual você está tentando construir.
Para deixar esse ponto absolutamente claro para todos e evitar mal-entendidos, observe que um plug-in Flutter é um pacote específico do Flutter que contém código específico da plataforma necessário para fornecer seus recursos.
Por exemplo, você pode usar o pacote url_launcher
desenvolvido pelo Google o quanto quiser (e talvez queira, já que a web é construída em hiperlinks).
Um exemplo de pacote desenvolvido pelo Google cujo uso impediria o desenvolvimento da Web é path_provider
, que é usado para obter o caminho de armazenamento local para salvar arquivos. Este é um exemplo de um pacote que, aliás, não é útil para um aplicativo da web, então não poder usá-lo não é realmente uma chatice, exceto pelo fato de que você precisa alterar seu código para para funcionar na web se você estiver usando.
Por exemplo, você pode usar o pacote shared_preferences, que depende do HTML localStorage
na web.
Advertências semelhantes são válidas em relação às plataformas de desktop: muito poucos plugins são compatíveis com plataformas de desktop e, como esse é um tema recorrente, muito mais trabalho precisa ser feito no lado da área de trabalho do que o realmente necessário no Flutter para a web.
Criando layouts responsivos no Flutter
Por causa do que descrevi acima e para simplificar, vou assumir no restante deste post que sua plataforma de destino é a web, mas os conceitos básicos também se aplicam ao desenvolvimento de desktop.
Apoiar a web tem benefícios e responsabilidades. Ser praticamente forçado a oferecer suporte a diferentes tamanhos de tela pode parecer uma desvantagem, mas considere que a execução do aplicativo nos navegadores da Web permite que você veja com muita facilidade como seu aplicativo ficará em telas de diferentes tamanhos e proporções, sem precisar executar separadamente emuladores de dispositivos móveis.
Agora, vamos falar de código. Como tornar seu aplicativo responsivo?
Existem duas perspectivas a partir das quais esta análise é feita:
- “Quais widgets estou usando ou posso usar que podem ou devem se adaptar a telas de diferentes tamanhos?”
- “Como posso obter informações sobre o tamanho da tela e como posso usá-las ao escrever o código da interface do usuário?”
Responderemos a primeira pergunta mais tarde. Vamos primeiro falar sobre o último, porque ele pode ser tratado com muita facilidade e está no centro da questão. Existem duas maneiras de fazer isso:
- Uma maneira é pegar as informações do
MediaQueryData
da raizMediaQuery
InheritedWidget
, que precisa existir na árvore de widgets para que um aplicativo Flutter funcione (é parte deMaterialApp/WidgetsApp/CupertinoApp
), que você pode obter, assim como qualquer outroInheritedWidget
, comMediaQuery.of(context)
, que possui uma propriedadesize
, que é do tipoSize
e que, portanto, possui duas propriedadeswidth
eheight
do tipodouble
. - A outra maneira é usar um
LayoutBuilder
, que é um widget construtor (assim como umStreamBuilder
ou umFutureBuilder
) que passa para a funçãobuilder
(junto com ocontext
) um objetoBoxConstraints
que possui as propriedadesminHeight
,maxHeight
,minWidth
emaxWidth
.
Aqui está um exemplo de DartPad usando o MediaQuery
para obter restrições, cujo código é o seguinte:
import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(context) => MaterialApp( home: MyHomePage() ); } class MyHomePage extends StatelessWidget { @override Widget build(context) => Scaffold( body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ Text( "Width: ${MediaQuery.of(context).size.width}", style: Theme.of(context).textTheme.headline4 ), Text( "Height: ${MediaQuery.of(context).size.height}", style: Theme.of(context).textTheme.headline4 ) ] ) ) ); }
E aqui está um usando o LayoutBuilder
para a mesma coisa:
import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(context) => MaterialApp( home: MyHomePage() ); } class MyHomePage extends StatelessWidget { @override Widget build(context) => Scaffold( body: LayoutBuilder( builder: (context, constraints) => Center( child: Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ Text( "Width: ${constraints.maxWidth}", style: Theme.of(context).textTheme.headline4 ), Text( "Height: ${constraints.maxHeight}", style: Theme.of(context).textTheme.headline4 ) ] ) ) ) ); }
Agora, vamos pensar em quais widgets podem se adaptar às restrições.
Em primeiro lugar, vamos pensar nas diferentes maneiras de dispor vários widgets de acordo com o tamanho da tela.
O widget que se adapta mais facilmente é o GridView
. Na verdade, um GridView
construído usando o construtor GridView.extent
nem precisa do seu envolvimento para ser responsivo, como você pode ver neste exemplo muito simples:
import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(context) => MaterialApp( home: MyHomePage() ); } class MyHomePage extends StatelessWidget { final List elements = [ "Zero", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "A Million Billion Trillion", "A much, much longer text that will still fit" ]; @override Widget build(context) => Scaffold( body: GridView.extent( maxCrossAxisExtent: 130.0, crossAxisSpacing: 20.0, mainAxisSpacing: 20.0, children: elements.map((el) => Card(child: Center(child: Padding(padding: EdgeInsets.all(8.0), child: Text(el))))).toList() ) ); }
import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(context) => MaterialApp( home: MyHomePage() ); } class MyHomePage extends StatelessWidget { final List elements = [ "Zero", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "A Million Billion Trillion", "A much, much longer text that will still fit" ]; @override Widget build(context) => Scaffold( body: GridView.extent( maxCrossAxisExtent: 130.0, crossAxisSpacing: 20.0, mainAxisSpacing: 20.0, children: elements.map((el) => Card(child: Center(child: Padding(padding: EdgeInsets.all(8.0), child: Text(el))))).toList() ) ); }
Você pode acomodar conteúdo de tamanhos diferentes alterando o maxCrossAxisExtent
.
Esse exemplo serviu principalmente para mostrar a existência do construtor GridView.extent
GridView
, mas uma maneira muito mais inteligente de fazer isso seria usar um GridView.builder
com um SliverGridDelegateWithMaxCrossAxisExtent
, neste caso onde os widgets a serem mostrados na grade são criados dinamicamente a partir de outra estrutura de dados, como você pode ver neste exemplo:
import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(context) => MaterialApp( home: MyHomePage() ); } class MyHomePage extends StatelessWidget { final List<String> elements = ["Zero", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "A Million Billion Trillion", "A much, much longer text that will still fit"]; @override Widget build(context) => Scaffold( body: GridView.builder( itemCount: elements.length, gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent( maxCrossAxisExtent: 130.0, crossAxisSpacing: 20.0, mainAxisSpacing: 20.0, ), itemBuilder: (context, i) => Card( child: Center( child: Padding( padding: EdgeInsets.all(8.0), child: Text(elements[i]) ) ) ) ) ); }
Um exemplo de GridView se adaptando a diferentes telas é minha página de destino pessoal, que é um aplicativo web Flutter muito simples que consiste em um GridView
com um monte de Cards
, assim como o código de exemplo anterior, exceto que os Cards
são um pouco mais complexos e maiores .
Uma mudança muito simples que poderia ser feita em aplicativos projetados para telefones seria substituir uma Drawer
por um menu permanente à esquerda quando houver espaço.
Por exemplo, poderíamos ter um ListView
de widgets, como o seguinte, que é usado para navegação:
class Menu extends StatelessWidget { @override Widget build(context) => ListView( children: [ FlatButton( onPressed: () {}, child: ListTile( leading: Icon(Icons.looks_one), title: Text("First Link"), ) ), FlatButton( onPressed: () {}, child: ListTile( leading: Icon(Icons.looks_two), title: Text("Second Link"), ) ) ] ); }
Em um smartphone, um lugar comum para usar seria dentro de uma Drawer
(também conhecida como menu de hambúrguer).
Alternativas para isso seriam o BottomNavigationBar
ou o TabBar
, em combinação com o TabBarView
, mas com ambos teríamos que fazer mais alterações do que o necessário com a gaveta, então vamos ficar com a gaveta.
Para mostrar apenas o Drawer
contendo o Menu
que vimos anteriormente em telas menores, você escreveria um código parecido com o seguinte snippet, verificando a largura usando o MediaQuery.of(context)
e passando um objeto Drawer
para o Scaffold
somente se for menos do que algum valor de largura que acreditamos ser apropriado para nosso aplicativo:
Scaffold( appBar: AppBar(/* ... \*/), drawer: MediaQuery.of(context).size.width < 500 ? Drawer( child: Menu(), ) : null, body: /* ... \*/ )
Agora, vamos pensar no body
do Scaffold
. Como amostra de conteúdo principal do nosso aplicativo, usaremos o GridView
que construímos anteriormente, que mantemos em um widget separado chamado Content
para evitar confusão:
class Content extends StatelessWidget { final List elements = ["Zero", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "A Million Billion Trillion", "A much, much longer text that will still fit"]; @override Widget build(context) => GridView.builder( itemCount: elements.length, gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent( maxCrossAxisExtent: 130.0, crossAxisSpacing: 20.0, mainAxisSpacing: 20.0, ), itemBuilder: (context, i) => Card( child: Center( child: Padding( padding: EdgeInsets.all(8.0), child: Text(elements[i]) ) ) ) ); }
class Content extends StatelessWidget { final List elements = ["Zero", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "A Million Billion Trillion", "A much, much longer text that will still fit"]; @override Widget build(context) => GridView.builder( itemCount: elements.length, gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent( maxCrossAxisExtent: 130.0, crossAxisSpacing: 20.0, mainAxisSpacing: 20.0, ), itemBuilder: (context, i) => Card( child: Center( child: Padding( padding: EdgeInsets.all(8.0), child: Text(elements[i]) ) ) ) ); }
Em telas maiores, o próprio corpo pode ser uma Row
que mostra dois widgets: o Menu
, restrito a uma largura fixa, e o Content
preenchendo o restante da tela.
Em telas menores, todo o body
seria o Content
.
Vamos envolver tudo em um SafeArea
e Center
porque às vezes os widgets de aplicativos da web Flutter, especialmente ao usar Row
se Column
s, acabam fora da área visível da tela, e isso é corrigido com SafeArea
e/ou Center
.
Isso significa que o body
do Scaffold
será o seguinte:
SafeArea( child:Center( child: MediaQuery.of(context).size.width < 500 ? Content() : Row( children: [ Container( width: 200.0, child: Menu() ), Container( width: MediaQuery.of(context).size.width-200.0, child: Content() ) ] ) ) )
Aqui está tudo isso junto:
import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(context) => MaterialApp( home: HomePage() ); } class HomePage extends StatelessWidget { @override Widget build(context) => Scaffold( appBar: AppBar(title: Text("test")), drawer: MediaQuery.of(context).size.width < 500 ? Drawer( child: Menu(), ) : null, body: SafeArea( child:Center( child: MediaQuery.of(context).size.width < 500 ? Content() : Row( children: [ Container( width: 200.0, child: Menu() ), Container( width: MediaQuery.of(context).size.width-200.0, child: Content() ) ] ) ) ) ); } class Menu extends StatelessWidget { @override Widget build(context) => ListView( children: [ FlatButton( onPressed: () {}, child: ListTile( leading: Icon(Icons.looks_one), title: Text("First Link"), ) ), FlatButton( onPressed: () {}, child: ListTile( leading: Icon(Icons.looks_two), title: Text("Second Link"), ) ) ] ); } class Content extends StatelessWidget { final List<String> elements = ["Zero", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "A Million Billion Trillion", "A much, much longer text that will still fit"]; @override Widget build(context) => GridView.builder( itemCount: elements.length, gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent( maxCrossAxisExtent: 130.0, crossAxisSpacing: 20.0, mainAxisSpacing: 20.0, ), itemBuilder: (context, i) => Card( child: Center( child: Padding( padding: EdgeInsets.all(8.0), child: Text(elements[i]) ) ) ) ); }
Essa é a maioria das coisas que você precisa como introdução geral à interface do usuário responsiva no Flutter. Grande parte de sua aplicação dependerá da interface do usuário específica do seu aplicativo, e é difícil identificar exatamente o que você pode fazer para tornar seu aplicativo responsivo, e você pode adotar muitas abordagens, dependendo de sua preferência. Agora, porém, vamos ver como podemos fazer um exemplo mais completo em um aplicativo responsivo, pensando em elementos comuns de aplicativo e fluxos de interface do usuário.
Colocando em contexto: tornando um aplicativo responsivo
Até agora, temos apenas uma tela. Vamos expandir isso em um aplicativo de duas telas com navegação baseada em URL!
Criando uma página de login responsiva
É provável que seu aplicativo tenha uma página de login. Como podemos tornar isso responsivo?
As telas de login em dispositivos móveis geralmente são bastante semelhantes entre si. O espaço disponível não é muito; normalmente é apenas uma Column
com algum Padding
ao redor de seus widgets, e contém TextField
s para digitar um nome de usuário e uma senha e um botão para fazer login. , um TextEditingController
para cada TextField
) página de login para um aplicativo móvel pode ser o seguinte:
Scaffold( body: Container( padding: const EdgeInsets.symmetric( vertical: 30.0, horizontal: 25.0 ), child: Column( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ Text("Welcome to the app, please log in"), TextField( decoration: InputDecoration( labelText: "username" ) ), TextField( obscureText: true, decoration: InputDecoration( labelText: "password" ) ), RaisedButton( color: Colors.blue, child: Text("Log in", style: TextStyle(color: Colors.white)), onPressed: () {} ) ] ), ), )
Parece bom em um dispositivo móvel, mas esses TextField
muito amplos começam a parecer chocantes em um tablet, quanto mais em uma tela maior. No entanto, não podemos simplesmente decidir sobre uma largura fixa porque os telefones têm tamanhos de tela diferentes e devemos manter um certo grau de flexibilidade.
Por exemplo, por meio de experimentação, podemos descobrir que a largura máxima deve ser 500. Bem, definiríamos as constraints
do Container
para 500 (usei um Container
em vez de Padding
no exemplo anterior porque sabia onde estava indo com isso ) e estamos prontos, certo? Na verdade não, porque isso faria com que os widgets de login ficassem no lado esquerdo da tela, o que pode ser ainda pior do que esticar tudo. Então, envolvemos um widget Center
, assim:
Center( child: Container( constraints: BoxConstraints(maxWidth: 500), padding: const EdgeInsets.symmetric( vertical: 30.0, horizontal: 25.0 ), child: Column(/* ... \*/) ) )
Isso já parece bom, e nem tivemos que usar um LayoutBuilder
ou o MediaQuery.of(context).size
. Vamos dar um passo adiante para fazer isso parecer muito bom, no entanto. Ficaria melhor, na minha opinião, se a parte do primeiro plano fosse de alguma forma separada do plano de fundo. Podemos conseguir isso dando uma cor de fundo ao que está por trás do Container
com os widgets de entrada e mantendo o Container
de primeiro plano em branco. Para torná-lo um pouco melhor, vamos evitar que o Container
se estenda para a parte superior e inferior da tela em dispositivos grandes, dar-lhe cantos arredondados e dar-lhe uma boa transição animada entre os dois layouts.
Tudo isso agora requer um LayoutBuilder
e um Container
externo para definir uma cor de fundo e adicionar preenchimento em todo o Container
e não apenas nas laterais apenas em telas maiores. Além disso, para tornar animada a alteração na quantidade de preenchimento, precisamos apenas transformar esse Container
externo em um AnimatedContainer
, o que requer uma duration
para a animação, que definiremos como meio segundo, que é Duration(milliseconds: 500)
em código.
Aqui está o exemplo de uma página de login responsiva:
class LoginPage extends StatelessWidget { @override Widget build(context) => Scaffold( body: LayoutBuilder( builder: (context, constraints) { return AnimatedContainer( duration: Duration(milliseconds: 500), color: Colors.lightGreen[200], padding: constraints.maxWidth < 500 ? EdgeInsets.zero : EdgeInsets.all(30.0), child: Center( child: Container( padding: EdgeInsets.symmetric( vertical: 30.0, horizontal: 25.0 ), constraints: BoxConstraints( maxWidth: 500, ), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(5.0), ), child: Column( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ Text("Welcome to the app, please log in"), TextField( decoration: InputDecoration( labelText: "username" ) ), TextField( obscureText: true, decoration: InputDecoration( labelText: "password" ) ), RaisedButton( color: Colors.blue, child: Text("Log in", style: TextStyle(color: Colors.white)), onPressed: () { Navigator.pushReplacement( context, MaterialPageRoute( builder: (context) => HomePage() ) ); } ) ] ), ), ) ); } ) ); }
Como você pode ver, também alterei o RaisedButton
do onPressed
para um retorno de chamada que nos leva a uma tela chamada HomePage
(que pode ser, por exemplo, a visualização que construímos anteriormente com um GridView
e um menu ou uma gaveta). Agora, porém, essa parte de navegação é o que vamos focar.
Rotas nomeadas: tornando a navegação do seu aplicativo mais parecida com um aplicativo da Web adequado
Uma coisa comum para os aplicativos da Web é a capacidade de alterar as telas com base na URL. Por exemplo, https://appurl/login
deve fornecer algo diferente de https://appurl/somethingelse
. O Flutter, na verdade, suporta rotas nomeadas , que têm dois propósitos:
- Em um aplicativo da web, eles têm exatamente esse recurso que mencionei na frase anterior.
- Em qualquer aplicativo, eles permitem que você predefina rotas para seu aplicativo e dê nomes a eles e, em seguida, navegue até eles apenas especificando seus nomes.
Para fazer isso, precisamos alterar o construtor MaterialApp
para um que se pareça com o seguinte:
MaterialApp( initialRoute: "/login", routes: { "/login": (context) => LoginPage(), "/home": (context) => HomePage() } );
E então podemos mudar para uma rota diferente usando Navigator.pushNamed(context, routeName)
e Navigator.pushReplacementNamed(context, routeName)
, em vez de Navigator.push(context, route)
e Navigator.pushReplacement(context, route)
.
Aqui está o aplicado ao aplicativo hipotético que construímos no restante deste artigo. Você realmente não pode ver as rotas nomeadas em ação no DartPad, então você deve tentar isso em sua própria máquina com flutter run
ou verificar o exemplo em ação:
import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(context) => MaterialApp( initialRoute: "/login", routes: { "/login": (context) => LoginPage(), "/home": (context) => HomePage() } ); } class LoginPage extends StatelessWidget { @override Widget build(context) => Scaffold( body: LayoutBuilder( builder: (context, constraints) { return AnimatedContainer( duration: Duration(milliseconds: 500), color: Colors.lightGreen[200], padding: constraints.maxWidth < 500 ? EdgeInsets.zero : const EdgeInsets.all(30.0), child: Center( child: Container( padding: const EdgeInsets.symmetric( vertical: 30.0, horizontal: 25.0 ), constraints: BoxConstraints( maxWidth: 500, ), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(5.0), ), child: Column( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ Text("Welcome to the app, please log in"), TextField( decoration: InputDecoration( labelText: "username" ) ), TextField( obscureText: true, decoration: InputDecoration( labelText: "password" ) ), RaisedButton( color: Colors.blue, child: Text("Log in", style: TextStyle(color: Colors.white)), onPressed: () { Navigator.pushReplacementNamed( context, "/home" ); } ) ] ), ), ) ); } ) ); } class HomePage extends StatelessWidget { @override Widget build(context) => Scaffold( appBar: AppBar(title: Text("test")), drawer: MediaQuery.of(context).size.width < 500 ? Drawer( child: Menu(), ) : null, body: SafeArea( child:Center( child: MediaQuery.of(context).size.width < 500 ? Content() : Row( children: [ Container( width: 200.0, child: Menu() ), Container( width: MediaQuery.of(context).size.width-200.0, child: Content() ) ] ) ) ) ); } class Menu extends StatelessWidget { @override Widget build(context) => ListView( children: [ FlatButton( onPressed: () {}, child: ListTile( leading: Icon(Icons.looks_one), title: Text("First Link"), ) ), FlatButton( onPressed: () {}, child: ListTile( leading: Icon(Icons.looks_two), title: Text("Second Link"), ) ), FlatButton( onPressed: () {Navigator.pushReplacementNamed( context, "/login");}, child: ListTile( leading: Icon(Icons.exit_to_app), title: Text("Log Out"), ) ) ] ); } class Content extends StatelessWidget { final List<String> elements = ["Zero", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "A Million Billion Trillion", "A much, much longer text that will still fit"]; @override Widget build(context) => GridView.builder( itemCount: elements.length, gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent( maxCrossAxisExtent: 130.0, crossAxisSpacing: 20.0, mainAxisSpacing: 20.0, ), itemBuilder: (context, i) => Card( child: Center( child: Padding( padding: EdgeInsets.all(8.0), child: Text(elements[i]) ) ) ) ); }
Avante Com Sua Aventura Flutter
Isso deve dar uma ideia do que você pode fazer com o Flutter em telas maiores, especificamente na web. É uma estrutura adorável, muito fácil de usar, e seu suporte multiplataforma extremo só torna mais essencial aprender e começar a usar. Então, vá em frente e comece a confiar no Flutter para aplicativos da web também!
Recursos adicionais
- “Conchas de desktop”, GitHub
O estado atual e sempre atualizado do Flutter na área de trabalho - “Suporte de desktop para Flutter”, Flutter
Informações sobre as plataformas de desktop totalmente suportadas - “Suporte da Web para Flutter”, Flutter
Informações sobre Flutter para a web - “Todas as Amostras”, Flutter
Uma lista com curadoria de amostras e aplicativos do Flutter