Mais recentemente, descobri o Flutter - uma nova estrutura do Google para o desenvolvimento de aplicativos móveis de plataforma cruzada - e até tive a oportunidade de mostrar o básico do Flutter a uma pessoa que nunca havia programado antes. O próprio Flutter foi escrito em Dart - um idioma nascido no navegador Chrome e que escapou para o mundo dos consoles - e isso me fez pensar "hmm, mas o Flutter poderia muito bem ser escrito em Go!".
Porque não Tanto o Go como o Dart foram criados pelo Google, ambos com linguagens compiladas digitadas - para rever alguns dos eventos de maneira um pouco diferente, o Go seria um excelente candidato para a implementação de um projeto de grande escala como o Flutter. Alguém dirá - não há classes, genéricos e exceções no Go, por isso não se encaixa.
Então, vamos fingir que o Flutter já está escrito em Go. Como será o código e, em geral, ele funcionará?

O que há de errado com o Dart?
Eu tenho seguido essa linguagem desde o seu início como uma alternativa ao JavaScript nos navegadores. O Dart foi incorporado ao navegador Chrome por algum tempo e a esperança era que ele substituísse o JS. Foi incrivelmente triste ler em março de 2015 que o suporte ao Dart foi removido do Chrome .
Dart em si é ótimo! Bem, basicamente, depois do JavaScript, qualquer idioma é ótimo, mas depois, digamos, Go, o Dart não é tão bonito. mas tudo bem. Possui todos os recursos concebíveis e inconcebíveis - classes, genéricos, exceções, futuros, espera assíncrona, loop de eventos, JIT / AOT, coletor de lixo, sobrecarga de função - nomeie qualquer recurso conhecido da teoria das linguagens de programação e, no Dart, ele terá uma alta proporção probabilidades. O Dart possui uma sintaxe especial para praticamente qualquer chip - uma sintaxe especial para getters / setters, uma sintaxe especial para construtores abreviados, uma sintaxe especial para uma sintaxe especial e muito mais.
Isso faz com que o Dart, à primeira vista, seja familiar para as pessoas que já haviam programado em qualquer linguagem de programação antes, e isso é ótimo. Mas, ao tentar explicar toda essa abundância de recursos especiais em um exemplo simples "Olá, mundo", descobri que isso, pelo contrário, dificulta o domínio.
- todos os recursos "especiais" da linguagem eram confusos - "um método especial chamado construtor", "sintaxe especial para inicialização automática", "sintaxe especial para parâmetros nomeados" etc.
- tudo "oculto" era confuso - "de que importação é essa função? está oculto, olhando para o código que você não pode descobrir", "por que existe um construtor nesta classe, mas não nesta classe? está lá, mas está oculto" e assim por diante
- tudo "ambíguo" confuso - "então, aqui para criar os parâmetros da função com ou sem nomes?", "deve ser const ou final?", "aqui use a sintaxe da função normal ou '' abreviado com seta ''", etc.
Em princípio, essa trindade - "especial", "oculta" e "ambígua" - não é ruim captura a essência do que as pessoas chamam de "mágica" nas linguagens de programação. Esses são recursos criados para simplificar a escrita do código, mas de fato complicam sua leitura e compreensão.
E esta é exatamente a área em que o Go assume uma posição fundamentalmente diferente de outros idiomas e mantém ferozmente a defesa. Go é uma linguagem quase sem mágica - a quantidade de "oculto", "especial" e "ambíguo" é minimizada. Mas o Go tem suas desvantagens.
O que há de errado com o Go?
Como estamos falando do Flutter, e essa é uma estrutura de interface do usuário, vamos considerar o Go como uma ferramenta para descrever e trabalhar com a interface do usuário. Em geral, as estruturas de interface do usuário são um tremendo desafio e quase sempre exigem soluções especializadas. Uma das abordagens mais frequentes na interface do usuário é a criação de DSL - linguagens específicas do domínio - implementadas na forma de bibliotecas ou estruturas personalizadas especificamente para as necessidades da interface do usuário. E na maioria das vezes você pode ouvir a opinião de que o Go é objetivamente uma linguagem ruim para o DSL.
Em essência, DSL significa criar uma nova linguagem - termos e verbos - na qual o desenvolvedor possa operar. O código deve descrever claramente os principais recursos da interface gráfica e seus componentes, ser flexível o suficiente para liberar a imaginação do designer e, ao mesmo tempo, ser rígido o suficiente para restringi-lo de acordo com certas regras. Por exemplo, você poderá colocar os botões em algum contêiner e colocar o ícone no lugar certo nesse botão, mas o compilador retornará um erro se você tentar inserir o botão, digamos, em texto.
Além disso, as linguagens para descrever a interface do usuário são frequentemente declarativas - dando a oportunidade de descrever a interface na forma de "o que eu gostaria de ver" e deixar a própria estrutura entender a partir de qual código e como executá-lo.
Alguns idiomas foram originalmente desenvolvidos com essas tarefas à vista, mas não o Go. Parece que escrever Flutter on Go será outra tarefa!
Oda Flutter
Se você não conhece o Flutter, recomendo que passe o próximo final de semana assistindo a vídeos educacionais ou lendo tutoriais, dos quais existem muitos. Porque Flutter, sem dúvida, reverte as regras do jogo no desenvolvimento de aplicativos móveis. E, provavelmente, não apenas móvel - já existem renderizadores (em termos de Flutter, incorporadores) para iniciar aplicativos Flutter como aplicativos nativos de dekstop e aplicativos da web .
É fácil de aprender, é lógico, vem com uma enorme biblioteca de belos widgets em Design de Materiais (e não apenas), possui uma comunidade grande e grande e um excelente ajuste (se você gosta da facilidade de trabalhar com go build/run/test
no Go, depois no Flutter você terá uma experiência semelhante).
Há um ano, eu precisava escrever um pequeno aplicativo móvel (para iOS e Android, é claro), e percebi que a complexidade de desenvolver um aplicativo de alta qualidade para ambas as plataformas é muito grande (o aplicativo não era a tarefa principal) - tive que terceirizar e pagar por isso. De fato, escrever uma aplicação simples, mas de alta qualidade e trabalhar em todos os dispositivos, era uma tarefa impossível, mesmo para uma pessoa com quase 20 anos de experiência em programação. E sempre foi um absurdo para mim.
Com o Flutter, reescrevi esse aplicativo às 15h, enquanto aprendia a estrutura em si do zero. Se alguém me dissesse que isso poderia ser um pouco mais cedo, eu não acreditaria.
A última vez que vi um aumento semelhante na produtividade com a descoberta de novas tecnologias foi há 5 anos quando descobri o Go. Aquele momento mudou minha vida.
Portanto, recomendo começar a aprender Flutter e este tutorial é muito bom .
"Olá, mundo" no Flutter
Quando você cria um novo aplicativo através da flutter create
, você obtém exatamente esse programa com um título, texto, contador e um botão que incrementa o contador.

Eu acho que este é um ótimo exemplo. para escrevê-lo em nosso imaginário Flutter on Go. Possui quase todos os conceitos básicos da estrutura na qual você pode testar a ideia. Vamos dar uma olhada no código (este é um arquivo):
import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( primarySwatch: Colors.blue, ), home: MyHomePage(title: 'Flutter Demo Home Page'), ); } } class MyHomePage extends StatefulWidget { MyHomePage({Key key, this.title}) : super(key: key); final String title; @override _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { int _counter = 0; void _incrementCounter() { setState(() { _counter++; }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Text( 'You have pushed the button this many times:', ), Text( '$_counter', style: Theme.of(context).textTheme.display1, ), ], ), ), floatingActionButton: FloatingActionButton( onPressed: _incrementCounter, tooltip: 'Increment', child: Icon(Icons.add), ), ); } }
Vamos analisar o código em partes, analisar o que e como ele se encaixa no Go e dar uma olhada nas várias opções que temos.
Traduzimos o código no Go
O começo será simples e direto - importe a dependência e inicie a função main()
. Nada complicado ou interessante aqui, a mudança é quase sintática:
package hello import "github.com/flutter/flutter" func main() { app := NewApp() flutter.Run(app) }
A única diferença é que, em vez de iniciar o MyApp()
- uma função que é um construtor que é uma função especial oculta dentro de uma classe chamada MyApp - simplesmente chamamos a função explícita e não oculta NewApp()
. Ela faz a mesma coisa, mas é muito mais claro explicar e entender o que é, como começa e como funciona.
Classes de widget
No Flutter, tudo consiste em widgets. Na versão Dart do Flutter, cada widget é implementado como uma classe que herda classes especiais para widgets do Flutter.
Não há classes no Go e, portanto, não há hierarquia de classes, porque o mundo não é orientado a objetos e ainda menos hierárquico. Para programadores familiarizados apenas com o modelo OOP orientado a classe, isso pode ser uma revelação, mas realmente não é. O mundo é um gráfico gigante entrelaçado de conceitos, processos e interações. Não é perfeitamente estruturado, mas não caótico, e tentar compactá-lo em uma hierarquia de classes é a maneira mais confiável de tornar a base de código ilegível e desajeitada - exatamente o que a maioria das bases de código é atualmente.

Eu realmente aprecio o Go, porque seus criadores se deram ao trabalho de repensar esse conceito onipresente de classes e implementaram no Go um conceito de POO muito mais simples e mais poderoso, que, por acaso, acabou se aproximando do que o criador do OOP, Alan Kay, tinha em mente .
No Go, representamos qualquer abstração na forma de uma estrutura de tipo específica:
type MyApp struct {
Na versão Dart do Flutter, o MyApp
deve herdar o StatelessWidget
e substituir o método de build
. Isso é necessário para resolver dois problemas:
- dê ao nosso widget (
MyApp
) algumas propriedades / métodos especiais - permitir que o Flutter chame nosso código no processo de compilação / renderização
Eu não conheço os elementos internos do Flutter, então digamos que o item número 1 não esteja em questão, e apenas precisamos fazê-lo. A Go possui uma solução tão única e óbvia para isso: tipos de incorporação :
type MyApp struct { flutter.Core
Este código adicionará todas flutter.Core
propriedades e métodos do flutter.Core
ao nosso tipo MyApp
. Eu o chamei de Core
vez de Widget
, porque, em primeiro lugar, a incorporação de tipo ainda não torna nosso MyApp
widget e, em segundo lugar, esse nome é muito bem usado na estrutura do GopherJS Vecty (algo como React, only for Go). Mais adiante, abordarei o tema da semelhança entre Vecty e Flutter.
O segundo ponto - a implementação do método build()
, que poderá usar o mecanismo Flutter - também é resolvido de forma simples e inequívoca no Go. Só precisamos adicionar um método com uma assinatura específica que satisfaça uma certa interface definida em algum lugar da nossa biblioteca fictícia Flutter no Go:
flutter.go:
type Widget interface { Build(ctx BuildContext) Widget }
E agora nosso main.go:
type MyApp struct { flutter.Core
Podemos notar algumas diferenças aqui:
- o código é um pouco mais detalhado -
BuildContext
, Widget
e MaterialApp
apontam para flutter
importações na frente deles. - o código é um pouco menos infundado - não há palavras como
extends Widget
ou @override
- O método
Build()
começa com uma letra maiúscula, porque significa a "publicidade" do método no Go. No Dart, a publicidade é determinada pelo fato de o nome começar com um sublinhado (_) ou não.
Portanto, para criar um widget em nosso Flutter on Go, precisamos incorporar o tipo flutter.Core
e implementar a interface flutter.Widget
. Nós descobrimos isso, cavar mais.
Condição
Essa foi uma das coisas que realmente me confundiu em Flutter. Existem duas classes diferentes - StatelessWidget
e StatefulWidget
. Quanto a mim, um "widget sem estado" é o mesmo widget, apenas sem, hmm, dados, estado - por que criar uma nova classe? Mas tudo bem, eu posso viver com isso.
Além disso, além disso, você não pode simplesmente herdar outra classe ( StatefulWidget
), mas deve escrever essa mágica (o IDE fará isso por você, mas não é o ponto):
class MyHomePage extends StatefulWidget { @override _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { int _counter = 0; void _incrementCounter() { setState(() { _counter++; }); } @override Widget build(BuildContext context) { return Scaffold() } }
Hmm, vamos ver o que acontece aqui.
Fundamentalmente, a tarefa é a seguinte: adicione um estado ao widget - o contador, no nosso caso - e informe o mecanismo Flutter quando alteramos o estado para redesenhar o widget. Essa é a real complexidade do problema (complexidade essencial em termos de Brooks).
Tudo o resto é complexidade acidental. O Flutter on Dart cria uma nova classe State
que usa genéricos e usa um widget como parâmetro de tipo. Em seguida, a classe _MyHomePageState
é _MyHomePageState
, que herda o State MyApp
... ok, você ainda pode digeri-lo de alguma forma. Mas por que o método build()
é definido pela classe State, e não a classe que o widget é? Brrr ....
A resposta a esta pergunta está nas Perguntas frequentes do Flutter e a resposta curta é considerada em detalhes suficientes aqui - para evitar uma certa classe de erros ao herdar o StatefulWidget
. Em outras palavras, essa é uma solução alternativa para resolver o problema do design de OOP orientado a classe. Chic.
Como faríamos isso no Go?
Em primeiro lugar, pessoalmente eu preferiria não criar uma entidade separada para o "estado" - State
. Afinal, já temos um estado em cada tipo específico - esses são apenas campos da estrutura. A linguagem já nos deu essa essência, por assim dizer. Criar outra entidade similar apenas confundirá o programador.
O desafio, é claro, é dar a Flutter a capacidade de responder a mudanças de estado (afinal, essa é a essência da programação reativa). E se podemos "pedir" ao desenvolvedor para usar uma função especial ( setState()
), então podemos pedir da mesma forma que use uma função especial para informar ao mecanismo quando redesenhar e quando não usar. No final, nem todas as alterações de estado exigem redesenho, e aqui teremos ainda mais controle:
type MyHomePage struct { flutter.Core counter int }
Você pode brincar com diferentes opções de nomes - eu gosto de NeedsUpdate()
por sua franqueza e pelo fato de ser uma propriedade de widget (obtida de flutter.Core
), mas o método global flutter.Rerender()
também parece bom. É verdade que dá uma falsa sensação de que o widget será redesenhado imediatamente, mas não é assim - será redesenhado na próxima atualização de quadro, e a frequência de chamada do método pode ser muito maior que a frequência de renderização - mas nosso mecanismo Flutter já deve ser capaz de lidar com isso.
Mas a idéia é que acabamos de resolver o problema necessário sem adicionar:
- novo tipo
- genéricos
- regras especiais para o estado de leitura / gravação
- novos métodos substituídos especiais
Além disso, a API é muito mais clara e compreensível - basta aumentar o contador (como faria em qualquer outro programa) e pedir ao Flutter para redesenhar o widget. Isso é algo que não é muito óbvio se setState
- que não é apenas uma função especial para definir estado, é uma função que retorna uma função (wtf?) Na qual já estamos fazendo algo com state. Mais uma vez, a magia oculta nas linguagens e estruturas torna muito difícil entender e ler o código.
No nosso caso, resolvemos o mesmo problema, o código é mais simples e duas vezes menor.
Widgets de estado em outros widgets
Como uma continuação lógica do tópico, vamos dar uma olhada em como o "widget de estado" é usado em outro widget no Flutter:
@override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', home: MyHomePage(title: 'Flutter Demo Home Page'), ); }
MyHomePage
aqui é um "widget de estado" (possui um contador), e nós o criamos chamando o construtor MyHomePage()
durante a construção ... Espere, o que?
build()
é chamado para redesenhar o widget, possivelmente várias vezes por segundo. Por que devemos criar um widget, especialmente com um estado, sempre durante a renderização? Isso não faz sentido.
Acontece que o Flutter usa essa separação entre Widget
e State
para ocultar esse gerenciamento de inicialização / estado do programador (mais coisas ocultas, mais!). Ele cria um novo widget sempre, mas o estado, se já tiver sido criado, é automaticamente encontrado e anexado ao widget. Essa mágica acontece de forma invisível e eu não tenho ideia de como funciona - você precisa ler o código.
Considero um verdadeiro mal na programação ocultar e esconder o máximo possível do programador, justificando-o com ergonomia. Estou certo de que o programador estatístico médio não lerá o código Flutter para entender como essa mágica funciona e é improvável que entenda como e o que está interconectado.
Para a versão Go, eu definitivamente não gostaria de uma feitiçaria oculta e preferiria uma inicialização explícita e visível, mesmo que isso signifique um código um pouco mais infundado. A abordagem de Flutter ao Dart também pode ser implementada, mas eu adoro o Go por minimizar a magia e gostaria de ver a mesma filosofia nos frameworks. Portanto, eu escreveria meu código para widgets com estado na árvore de widgets assim:
Esse código perde a versão do Dart, pois se eu quiser remover o homePage
da árvore de widgets e substituí-lo por outra coisa, preciso removê-lo em três locais, em vez de um. Mas, em troca, temos uma imagem completa do que, onde e como isso acontece, onde a memória está alocada, quem liga para quem e assim por diante - o código na palma da sua mão é claro e fácil de ler.
A propósito, o Flutter também possui o StatefulBuilder , que adiciona ainda mais magia e permite criar widgets com o estado em movimento.
DSL
Agora vamos assumir a parte divertida. Como vamos representar a árvore de widgets no Go? Queremos que pareça conciso, limpo, fácil de refatorar e alterar, descreva as relações espaciais entre widgets (widgets visualmente próximos, devem estar próximos e na descrição) e, ao mesmo tempo, suficientemente flexíveis para descrever arbitrárias código como manipuladores de eventos.
Parece-me que a opção no Dart é bastante bonita e eloquente:
return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Text('You have pushed the button this many times:'), Text( '$_counter', style: Theme.of(context).textTheme.display1, ), ], ), ), floatingActionButton: FloatingActionButton( onPressed: _incrementCounter, tooltip: 'Increment', child: Icon(Icons.add), ), );
Cada widget possui um construtor que aceita parâmetros opcionais, e o que torna o registro realmente bom aqui são os parâmetros nomeados das funções .
Parâmetros nomeados
Caso você não esteja familiarizado com esse termo, em muitos idiomas os parâmetros da função são chamados de "posicionais", pois sua posição é importante para a função:
Foo(arg1, arg2, arg3)
, e no caso de parâmetros nomeados, tudo é decidido pelo nome na chamada:
Foo(name: arg1, description: arg2, size: arg3)
Isso adiciona texto, mas salva cliques e se move pelo código, na tentativa de entender o que significam os parâmetros.
No caso da árvore de widgets, eles desempenham um papel fundamental na legibilidade. Compare o mesmo código acima, mas sem parâmetros nomeados:
return Scaffold( AppBar( Text(widget.title), ), Center( Column( MainAxisAlignment.center, <Widget>[ Text('You have pushed the button this many times:'), Text( '$_counter', Theme.of(context).textTheme.display1, ), ], ), ), FloatingActionButton( _incrementCounter, 'Increment', Icon(Icons.add), ), );
Não é isso. certo? Não é apenas mais difícil de entender (você precisa ter em mente o que cada parâmetro significa e qual é o seu tipo, e isso é uma carga cognitiva significativa), mas também não nos dá liberdade na escolha de quais parâmetros queremos transferir. Por exemplo, você pode não querer um FloatingActionButton
para seu aplicativo Material, portanto, simplesmente não o especifica nos parâmetros. Sem parâmetros nomeados, precisaremos forçar a especificação de todos os widgets possíveis ou recorrer à mágica com reflexão para descobrir quais widgets foram transferidos.
E como não há sobrecarga de funções e parâmetros nomeados no Go, essa não será uma tarefa fácil para o Go.
Árvore de widgets Go
Versão 1
Vamos dar uma olhada no objeto Andaime , que é um invólucro conveniente para um aplicativo móvel. Possui várias propriedades - appBar, drawe, home, bottomNavigationBar, floatingActionBar - e todos esses são widgets. Ao criar uma árvore de widgets, precisamos, de alguma forma, inicializar esse objeto, passando as propriedades de widget acima mencionadas. Bem, isso não é muito diferente da criação e inicialização usual de objetos.
Vamos tentar a abordagem da testa:
return flutter.NewScaffold( flutter.NewAppBar( flutter.Text("Flutter Go app", nil), ), nil, nil, flutter.NewCenter( flutter.NewColumn( flutter.MainAxisCenterAlignment, nil, []flutter.Widget{ flutter.Text("You have pushed the button this many times:", nil), flutter.Text(fmt.Sprintf("%d", m.counter), ctx.Theme.textTheme.display1), }, ), ), flutter.FloatingActionButton( flutter.NewIcon(icons.Add), "Increment", m.onPressed, nil, nil, ), )
Não é o código de interface do usuário mais bonito, definitivamente. A palavra flutter
todo lugar e pergunta. para escondê-lo (na verdade, eu tive que nomear o material
da embalagem, não a flutter
, mas não a essência), os parâmetros anônimos são completamente óbvios e esses nil
são abertamente confusos em todos os lugares.
Versão 2
Como, no entanto, a maior parte do código usará um ou outro tipo / função do pacote flutter
, podemos usar o formato "dot import" para importar o pacote para o nosso espaço para nome e, assim, "ocultar" o nome do pacote:
import . "github.com/flutter/flutter"
Agora, em vez de flutter.Text
, podemos escrever apenas Text
. Geralmente, é uma prática ruim, mas trabalhamos com a estrutura, e essa importação estará literalmente em todas as linhas. Na minha prática, esse é exatamente o caso para o qual essa importação é aceitável - por exemplo, como ao usar a maravilhosa estrutura para testar o GoConvey .
Vamos ver como o código ficará:
return NewScaffold( NewAppBar( Text("Flutter Go app", nil), ), nil, nil, NewCenter( NewColumn( MainAxisCenterAlignment, nil, []Widget{ Text("You have pushed the button this many times:", nil), Text(fmt.Sprintf("%d", m.counter), ctx.Theme.textTheme.display1), }, ), ), FloatingActionButton( NewIcon(icons.Add), "Increment", m.onPressed, nil, nil, ), )
Já é melhor, mas esses parâmetros nulos e sem nome ....
Versão 3
Vamos ver como será o código se usarmos a reflexão (a capacidade de inspecionar o código enquanto o programa estiver em execução) para analisar os parâmetros passados. Essa abordagem é usada em várias estruturas HTTP iniciais no Go ( martini , por exemplo) e é considerada uma prática muito ruim - é insegura, perde a conveniência do sistema de tipos, é relativamente lenta e adiciona mágica ao código - mas, para fins de experimento, você pode tentar:
return NewScaffold( NewAppBar( Text("Flutter Go app"), ), NewCenter( NewColumn( MainAxisCenterAlignment, []Widget{ Text("You have pushed the button this many times:"), Text(fmt.Sprintf("%d", m.counter), ctx.Theme.textTheme.display1), }, ), ), FloatingActionButton( NewIcon(icons.Add), "Increment", m.onPressed, ), )
Não é ruim, e parece com a versão original do Dart, mas a falta de parâmetros nomeados ainda machuca os olhos.
Versão 4
Vamos recuar um pouco e nos perguntar o que estamos tentando fazer. Não precisamos copiar cegamente a abordagem do Dart (embora seja um bom bônus - há menos para ensinar as pessoas que já estão familiarizadas com o Flutter on Dart). De fato, apenas criamos novos objetos e atribuímos propriedades a eles.
Pode tentar assim?
scaffold := NewScaffold() scaffold.AppBar = NewAppBar(Text("Flutter Go app")) column := NewColumn() column.MainAxisAlignment = MainAxisCenterAlignment counterText := Text(fmt.Sprintf("%d", m.counter)) counterText.Style = ctx.Theme.textTheme.display1 column.Children = []Widget{ Text("You have pushed the button this many times:"), counterText, } center := NewCenter() center.Child = column scaffold.Home = center icon := NewIcon(icons.Add), fab := NewFloatingActionButton() fab.Icon = icon fab.Text = "Increment" fab.Handler = m.onPressed scaffold.FloatingActionButton = fab return scaffold
, " ", . -, – , . -, , .
, UI GTK Qt . , , Qt 5:
QGridLayout *layout = new QGridLayout(this); layout->addWidget(new QLabel(tr("Object name:")), 0, 0); layout->addWidget(m_objectName, 0, 1); layout->addWidget(new QLabel(tr("Location:")), 1, 0); m_location->setEditable(false); m_location->addItem(tr("Top")); m_location->addItem(tr("Left")); m_location->addItem(tr("Right")); m_location->addItem(tr("Bottom")); m_location->addItem(tr("Restore")); layout->addWidget(m_location, 1, 1); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); layout->addWidget(buttonBox, 2, 0, 1, 2);
, - . , , , .
5
, – -. Por exemplo:
func Build() Widget { return NewScaffold(ScaffoldParams{ AppBar: NewAppBar(AppBarParams{ Title: Text(TextParams{ Text: "My Home Page", }), }), Body: NewCenter(CenterParams{ Child: NewColumn(ColumnParams{ MainAxisAlignment: MainAxisAlignment.center, Children: []Widget{ Text(TextParams{ Text: "You have pushed the button this many times:", }), Text(TextParams{ Text: fmt.Sprintf("%d", m.counter), Style: ctx.textTheme.display1, }), }, }), }), FloatingActionButton: NewFloatingActionButton( FloatingActionButtonParams{ OnPressed: m.incrementCounter, Tooltip: "Increment", Child: NewIcon(IconParams{ Icon: Icons.add, }), }, ), }) }
! , . ...Params
, . , , Go , , .
-, ...Params
, . (proposal) — " " . , FloatingActionButtonParameters{...}
{...}
. :
func Build() Widget { return NewScaffold({ AppBar: NewAppBar({ Title: Text({ Text: "My Home Page", }), }), Body: NewCenter({ Child: NewColumn({ MainAxisAlignment: MainAxisAlignment.center, Children: []Widget{ Text({ Text: "You have pushed the button this many times:", }), Text({ Text: fmt.Sprintf("%d", m.counter), Style: ctx.textTheme.display1, }), }, }), }), FloatingActionButton: NewFloatingActionButton({ OnPressed: m.incrementCounter, Tooltip: "Increment", Child: NewIcon({ Icon: Icons.add, }), }, ), }) }
Dart! .
6
, . , , , , .
, , , -, – :
button := NewButton(). WithText("Click me"). WithStyle(MyButtonStyle1)
ou
button := NewButton(). Text("Click me"). Style(MyButtonStyle1)
Scaffold- :
Go – , . Dart-, :
New...()
– , . , — " , , , , , , – " .
, , 5- 6- .
"hello, world" Flutter Go:
main.go
package hello import "github.com/flutter/flutter" func main() { flutter.Run(NewMyApp()) }
app.go:
package hello import . "github.com/flutter/flutter"
home_page.go:
package hello import ( "fmt" . "github.com/flutter/flutter" )
!
Conclusão
Vecty
, , Vecty . , , , , Vecty DOM/CSS/JS, Flutter , 120 . , Vecty , Flutter Go Vecty .
Flutter
– , . Flutter, .
Go
" Flutter Go?" "" , , , , , Flutter, , , "" . , Go .
, Go . . Go, , , -. – , , .
Go. – , .
Flutter
, Flutter , , . "/ " , Dart ( , , ). Dart, , (, ) DartVM V8, Flutter – Flutter -.
, . . , , 1.0 . , - .
game changer, Flutter , , .
UI – Flutter, .
Referências