Flutter. Prós e contras



Primeiro de tudo, vamos falar um pouco sobre o que é Flutter. Essa é uma estrutura para criar aplicativos móveis do Google. É multiplataforma e permite compilar o projeto criado para 3 sistemas operacionais:
  • Android
  • iOS
  • Fúcsia

Além disso, para o sistema operacional mais recente - Fuchsia - essa é a única maneira de criar um aplicativo.
O Flutter há muito tempo, desde 2015, foi apresentado apenas nas versões alfa e beta. A primeira versão estável foi lançada em 4 de dezembro de 2018.


O Flutter é promovido ativamente pelo Google, ganhando popularidade gradualmente e, provavelmente, continuará a ofuscar outras ferramentas de desenvolvimento de plataforma cruzada atualmente em uso (React Native, Xamarin), especialmente se o Fuchsia for amplamente distribuído. Como o Google posiciona esse sistema operacional como um substituto para o Android, mais cedo ou mais tarde o Flutter substituirá o desenvolvimento nativo do Android. Portanto, perspectivas e desenvolvimento ativo são as principais vantagens do Flutter.


+ Perspectiva e desenvolvimento ativo


Vamos ver como isso funciona.


Na linguagem de programação Dart, um aplicativo móvel é criado com uma descrição da interface gráfica e toda a lógica do trabalho. O resultado do trabalho é adicionado ao aplicativo nativo, como imagens, fontes e afins (é claro, esse processo é automatizado).


Ao mesmo tempo, na parte nativa do aplicativo, uma única tela é criada onde a máquina virtual Dart é carregada, que executa o Flutter.


Observe que um dos pontos negativos de Flutter segue a partir daqui:


- O pacote de instalação final é maior, à medida que a máquina virtual Dart é adicionada a ele.


Assim, existem arquivos Flutter e máquinas virtuais adicionadas, dependendo do que compila - iOS ou Android.


A máquina virtual possui seu próprio mecanismo gráfico, desenha a interface do aplicativo com todas as transições entre telas, diálogos, fragmentos, etc. Nisso, o desenvolvimento no Flutter é significativamente diferente do desenvolvimento com o Xamarin e o React Native, que usam componentes reais do Android e iOS. No caso deles, é impossível usar componentes específicos da plataforma (se houver essa necessidade, é necessário criar duas opções para a interface do usuário). Ao escolher um design com o Flutter, basta focar em uma plataforma (por exemplo, Android). Ao criar um projeto para iOS, você verá uma interface Android padrão. Parece um pouco estranho e inesperado, mas bastante funcional (posteriormente, a interface pode ser melhorada).


+ Motor gráfico próprio (não é necessário criar uma interface separadamente para Android e iOS)


Agora sobre as impressões.


Ao portar vários aplicativos do Android para o Flutter, observamos algumas diferenças, que podem ser consideradas um mais e um menos.


A primeira coisa que chama sua atenção é uma maneira de criar telas, que é significativamente diferente daquela usada no Android e iOS. No Android, a lógica e a interface são separadas: a lógica é definida por código e a interface é definida pelo layout em xml. No Flutter, tudo isso é definido usando o código. Embora aqui seja usado um estilo especial para a interface - os elementos da interface são criados aninhados um no outro. É um pouco como o layout, de uma maneira muito semelhante ao React Native. No entanto, não há possibilidade de acesso direto aos elementos. Para alterar algo na tela, você deve atualizar a tela inteira ou usar controladores especiais adicionados antecipadamente ao widget durante sua criação.


- A interface é criada usando código, o que torna a linha entre lógica e design muito mais fina.


Por outro lado, essa abordagem facilita a divisão de telas em componentes separados. De fato, qualquer bloco de elementos de interface aninhados pode ser movido para um widget separado em apenas algumas etapas, e isso é muito mais fácil do que criar View e fragmentos personalizados.


+ A interface é facilmente dividida em módulos separados


As duas últimas observações provavelmente valem a pena ser examinadas. Para fazer isso, escreveremos um aplicativo simples que demonstra alguns dos recursos do Flutter. Este será um aplicativo com uma barra de navegação e barra de guias padrão.


Vamos fazer três abas:


1) Primeiro - com texto e controles deslizantes para ajustar o tamanho e a cor do texto
2) Adicione uma imagem para download à segunda (com indicador de progresso)
3) Em terceiro lugar, uma lista de exemplos




Suponha também que inicialmente não dividimos a interface do aplicativo em guias separadas. No Flutter, você pode implementar essa tela sem usar fragmentos. Obviamente, essa partição ainda é desejável, mas suponha que eles tenham esquecido de fazê-lo, ou que o design tenha sido alterado, após o qual o desenvolvimento foi continuado por inércia por uma classe.

Agora considere o código fonte que implementa esse layout:


void main() => runApp(MyApp()); class MyApp extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( primarySwatch: Colors.blue, ), home: MyHomePage(title: 'Home Page'), ); } } class MyHomePage extends StatefulWidget { MyHomePage({Key key, this.title}) : super(key: key); final String title; @override _MyHomePageState createState() => _MyHomePageState(); } 

Esse fragmento de código é padrão para quase qualquer aplicativo Flutter (é criado junto com o projeto).


MyApp é uma classe do próprio aplicativo, que descreve os parâmetros gerais ao criar MaterialApp: nome, fontes, cores e estilos do aplicativo. A tela principal do aplicativo também é indicada aqui (para nós, esse é o MyHomePage).


Vamos fazer uma observação importante: os widgets do Flutter são divididos em dois tipos:


1) StatefulWidget
2) StatelessWidget


São necessárias duas classes para descrever o StatefulWidget: a classe do próprio widget e a classe do seu estado (no qual o trabalho principal ocorrerá).


StatelessWidget é descrito por uma classe com um estado fixo e só pode ser alterado recriando a partir do widget principal. Portanto, para nossos propósitos, StatefulWidget é necessário.


Agora considere _MyHomePageState:


 class _MyHomePageState extends State<MyHomePage> { int _currentIndex = 0; double _size = 14; double _r = 0; double _g = 0; double _b = 0; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: <Widget>[ 

Para facilitar a percepção, uma guia com texto é marcada em vermelho, uma guia com uma imagem em verde, uma guia de lista em azul e um menu de navegação em amarelo. Como você pode ver, a interface é descrita como um conjunto de widgets (e suas matrizes) incorporados um ao outro:










Funções utilizadas:
  void _onTapped(int index) { setState(() { _currentIndex = index; }); } void _setTextStyle( {double size = -1, double r = -1, double g = -1, double b = -1}) { setState(() { if (size > 0) { _size = size; } if (r > 0) { _r = r; } if (g > 0) { _g = g; } if (b > 0) { _b = b; } }); } } 

Vamos considerá-los com mais detalhes:


onTapped - uma função chamada ao alternar uma guia no menu inferior. Ele chama a função especial setState, que permite atualizar o widget atual com novos dados (e atualizamos a variável _currentIndex).


Vamos ver onde se aplica:


 body: <Widget>[    ][_currentIndex] 

Aqui estamos lidando com uma matriz, de onde, usando _currentIndex, uma das opções de layout da tela é selecionada e substituída como uma das guias.


Em seguida, vem a função _setTextStyle. Ele tem um anúncio muito incomum para idiomas do tipo C.


 void _setTextStyle({double size = -1, double r = -1, double g = -1,double b = -1}) 

Os argumentos são passados ​​para ele não como uma lista, mas como uma matriz. Esse é um dos recursos muito interessantes do Dart, que permite criar funções com um número variável de argumentos muito mais finamente do que o que acontece na maioria dos outros idiomas.


Como cada argumento é nomeado, podemos levá-los em ordem aleatória. Por exemplo:


 _setTextStyle(size: 24, b: 255) 

Vamos dividir a classe da tela grande em widgets. É melhor dividir por elementos lógicos; no nosso caso, são abas. Graças aos recursos do Flutter, para isso, basta pegar os fragmentos de código responsáveis ​​por cada guia e transferi-los juntamente com a lógica para separar as classes usando o método build.


Primeira aba:


 class TextWidget extends StatefulWidget { @override _TextWidgetState createState() => _TextWidgetState(); } class _TextWidgetState extends State<TextWidget> { double _size = 14; double _r = 0; double _g = 0; double _b = 0; @override Widget build(BuildContext context) { return Column( children: <Widget>[ Text("Example String", style: TextStyle( fontSize: _size, color: Color.fromRGBO(_r.toInt(), _g.toInt(), _b.toInt(), 1))), Container(constraints: BoxConstraints.expand(height: 32.0)), Slider( label: "${_size.toInt()} sp", value: _size, min: 10, max: 48, divisions: 38, activeColor: Colors.black, inactiveColor: Colors.grey, onChanged: (val) => _setTextStyle(size: val)), Slider( label: _r.toInt().toString(), value: _r, min: 0, max: 255, divisions: 255, activeColor: Colors.red, inactiveColor: Colors.grey, onChanged: (val) => _setTextStyle(r: val), ), Slider( label: _g.toInt().toString(), value: _g, min: 0, max: 255, divisions: 255, activeColor: Colors.green, inactiveColor: Colors.grey, onChanged: (val) => _setTextStyle(g: val), ), Slider( label: _b.toInt().toString(), value: _b, min: 0, max: 255, divisions: 256, activeColor: Colors.blue, inactiveColor: Colors.grey, onChanged: (val) => _setTextStyle(b: val), ), ], ); } } 

Como o widget precisa ser atualizado (método _setTextStyle), usamos StatefulWidget.
Não há necessidade de atualização para as próximas duas guias, portanto, usaremos StatelessWidget.


Segunda aba:


 class ImageWidget extends StatelessWidget { @override Widget build(BuildContext context) { return Stack( children: <Widget>[ Center(child: CircularProgressIndicator()), Center( child: FadeInImage.memoryNetwork( placeholder: kTransparentImage, image: 'https://picsum.photos/250?image=9', ), ), ], ); } } 

Terceira guia:


 class ListWidget extends StatelessWidget { @override Widget build(BuildContext context) { return ListView.builder( itemCount: 25, itemBuilder: (BuildContext context, int index) { return Container( child: Text( 'entry $index', style: TextStyle(color: Colors.white), ), margin: EdgeInsets.all(16.0), padding: EdgeInsets.all(16.0), decoration: BoxDecoration( color: Colors.blue, borderRadius: BorderRadius.all( Radius.circular(16.0), ), ), ); }, ); } } 

Código de status da tela principal alterado:


 class _MyHomePageState extends State<MyHomePage> { int _currentIndex = 0; Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title), actions: <Widget>[ IconButton(icon: Icon(Icons.navigate_next), onPressed: next) ], ), body: <Widget>[ TextWidget(), ImageWidget(), ListWidget(), ][_currentIndex], bottomNavigationBar: BottomNavigationBar( currentIndex: _currentIndex, onTap: _onTapped, items: [ BottomNavigationBarItem( icon: new Icon(Icons.text_format), title: new Text('Text'), ), BottomNavigationBarItem( icon: new Icon(Icons.image), title: new Text('Image'), ), BottomNavigationBarItem( icon: Icon(Icons.list), title: Text('ListView'), ) ], )); } 

Assim, dividimos facilmente uma tela grande em uma tela pequena e três widgets pequenos. Você pode notar que, nas telas Flutter, conceitualmente, não são diferentes dos widgets (mais precisamente, os widgets assumem as funções de atividade, fragmento e exibição personalizada). Esse recurso é muito conveniente quando o aplicativo precisa de uma visualização em tela cheia de qualquer elemento - para isso, você pode usar nossos widgets com o mínimo de refinamento.


E, no entanto, existem diferenças mínimas entre um widget usado como tela e um widget regular. O elemento raiz do widget de tela deve ser um objeto Andaime (permite adicionar appBar, bottomNavigationBar, floatingActionButton, gaveta etc.).


Os widgets regulares não têm essa limitação, pois, usando o método de construção, eles serão incorporados na tela principal, onde o Scaffold já está disponível.


Felizmente, adicionar Andaime a um widget regular não afeta seu desempenho.


Você também pode adicionar SafeArea (para fornecer um recuo para a barra de status). A seguinte conversão simples é obtida:


De:


 @override Widget build(BuildContext context) { return []; } 

Para:


 @override Widget build(BuildContext context) { return Scaffold( body: SafeArea( child: [] ), ); } 

Bem, agora voltemos à discussão dos prós e contras de Flutter.


Flutter foi lançado recentemente, então os bugs são comuns o suficiente. Isso é especialmente perceptível ao atualizar o Flutter - algumas bibliotecas começam a trabalhar com erros.


- Instabilidade (recentemente deixada a versão beta)


Obviamente, ao usar a nova estrutura, você tem muito menos bibliotecas à sua disposição do que no desenvolvimento nativo do Android / iOS. No entanto, ainda existem muitas bibliotecas para o Flutter, e elas continuam aparecendo com grande velocidade. Por exemplo, muitas bibliotecas foram adicionadas no segundo semestre de 2018, aparentemente, em preparação para a primeira versão estável, e as bibliotecas mais importantes (Google Analytics, Firebase, Maps etc.) já existiam antes disso.


- Menos bibliotecas do que para desenvolvimento nativo
+ As bibliotecas mais importantes já estão lá, novas estão surgindo constantemente


É hora de fazer um balanço! Vamos relembrar todos os prós e contras, organizando os elementos das vantagens mais significativas aos menos significativos:


+ Plataforma cruzada
+ Perspectiva e desenvolvimento ativo
+ As bibliotecas mais importantes já estão lá, novas estão surgindo constantemente
+ Próprio mecanismo gráfico
+ A interface é facilmente dividida em módulos separados


- O pacote de instalação final é maior, pois a máquina virtual Dart é adicionada a ele
- A interface é criada usando código, o que torna a linha entre lógica e design muito mais fina
- Menos bibliotecas (e informações) do que para o desenvolvimento nativo
- Instabilidade (recentemente deixada a versão beta)


Obrigado pela atenção!

Source: https://habr.com/ru/post/pt441766/


All Articles