Meu aprendizado em Dart! Introdução: parte 1!
Antes de começar a ler…
A ideia inicial era criar um artigo/tutorial, que contivesse os conhecimentos basico de Dart, e também algumas aplicações no final, mas, acabei percebendo que começou a ficar muito extenso, e por esse motivo decidi dividir em partes, essa primeira em até cinco partes ao longo da semana, sendo desacoplada de qualquer outro conteúdo, sendo como um “Backup” do meu aprendizado… Sendo originalmente mais de 80000(oitenta mil) caracteres para esse texto...
Mostrando como eu aprendo uma nova linguagem, tecnologia, framework ou qualquer conhecimento técnico…
Antes que perguntem, todas as aplicações que seriam adicionadas a este artigo/tutorial, teriam sidos retiradas da documentação.
E como eu sempre falo, leia a documentação oficial, se for possível para você!
Até o atual momento da publicação deste texto, o TabNews, não formata o código em Dart corretamente, peço de antemão desculpas por qualquer inconveniência causada pela formatação incorreta do código.
Contextualizando
Uns dias atrás, eu simplesmente tive a vontade de aprender Dart e Flutter, só que prefiro aprender primeiro o Dart. Sempre que vou aprender alguma coisa nova, eu estudo diretamente na fonte, que neste caso, seria a documentação do Dart.
Esse, provavelmente, será um artigo/tutorial extremamente longo, pois colocarei todo o que eu estiver aprendendo ou lendo diretamente da documentação do Dart. Sim, estou escrevendo isso enquanto estou fazendo todo o conteúdo que estiver disponível na documentação.
O que é Dart?
Recomendação
Para uma contextualização rápida, assistir dois vídeos do canal Código Fonte TV, um sobre Dart e outro sobre o Flutter.
Página inicial do site / Overview
Dart é uma linguagem otimizada para o cliente, para aplicativos rápidos em qualquer plataforma, desenvolva com uma linguagem de programação especializada em torno das necessidades de criação de interfaces de usuário, com compilação para código de máquina ARM e x64 para dispositivos móveis, desktop e back-end. Ou compilação para JavaScript para a web.
Dart também forma a base do Flutter . Ele fornece a linguagem e os tempos de execução que alimentam os aplicativos Flutter, mas o Dart também oferece suporte a muitas tarefas principais do desenvolvedor, como formatação, análise e teste de código.
Uma linguagem de programação fácil de aprender, com uma sintaxe familiar:
Dart
class Segment {
int links = 4;
toString() => “I have $links links”;
}
Kotlin
class Segment {
var links: Int = 4
override fun toString() = “I have $links links”
}
Swift
class Segment: CustomStringConvertible {
var links: Int = 4
public var description: String {
return “I have \(links) links”
}
}
TypeScript
class Segment {
links: number = 4
public toString = () : string => {
return `I have ${this.links} links`
};
}
A linguagem
Ele usa a verificação de tipo estático para garantir que o valor de uma variável sempre corresponda ao tipo estático da variável. Embora os tipos sejam obrigatórios, as anotações de tipo são opcionais devido à inferência de tipo.
Neste ponto no overview, é apresentado um exemplo de código, mostra vários recursos da linguagem, até o momento poderia pensar: “Tudo bem, vamos ver esse código”. Só que ele apresenta algo chamado “Método de Monte Carlo”, em relação à linguagem, ele não é complicado de se entender. Irei mostrar o código e logo em seguida tentarei explicar por partes, para você e para que eu possa entender neste primeiro momento.
Designa-se por método de Monte Carlo qualquer método de uma classe de métodos estatísticos que se baseiam em amostragens aleatórias massivas para obter resultados numéricos. Em suma, utilizam a aleatoriedade de dados para gerar um resultado para problemas que a priori são determinísticos. - Wikipédia
Calcule π usando o método de Monte Carlo.
import 'dart:math' show Random;
void main() async {
print('Compute π using the Monte Carlo method.');
await for (final estimate in computePi().take(100)) {
print('π ≅ $estimate');
}
}
/// Generates a stream of increasingly accurate estimates of π.
Stream<double> computePi({int batch = 100000}) async* {
var total = 0; // Inferred to be of type int
var count = 0;
while (true) {
final points = generateRandom().take(batch);
final inside = points.where((p) => p.isInsideUnitCircle);
total += batch;
count += inside.length;
final ratio = count / total;
// Area of a circle is A = π⋅r², therefore π = A/r².
// So, when given random points with x ∈ <0,1>,
// y ∈ <0,1>, the ratio of those inside a unit circle
// should approach π / 4. Therefore, the value of π
// should be:
yield ratio * 4;
}
}
Iterable<Point> generateRandom([int? seed]) sync* {
final random = Random(seed);
while (true) {
yield Point(random.nextDouble(), random.nextDouble());
}
}
class Point {
final double x;
final double y;
const Point(this.x, this.y);
bool get isInsideUnitCircle => x * x + y * y <= 1;
}
import 'dart:math' show Random;
Importação de bibliotecas, o Random
parece que ele é um método do dart:math
, fica exposto para ser chamado em qualquer lugar dessa parte do código por conta do show
. Onde, neste código, dá para perceber isso.
void main() async {
print('Compute π using the Monte Carlo method.');
await for (final estimate in computePi().take(100)) {
print('π ≅ $estimate');
}
}
Aparentemente a função main
, é da mesma maneira que no Java, que é a primeira coisa a ser executada, ele precisa de um porta de entrada em comum com a aplicação. Que neste caso usa um async
, para que ele possa ser assíncrono. O método print
para escrever no console. Com um for
, laço de repetição, onde o computePi().take(100)
é colocada no estimate
, até que todos os itens da matriz tenham a sua saída com o print
. Sendo que, provavelmente o .take(100)
, pega os 100(cem) primeiros itens da matriz.
Stream<double> computePi({int batch = 100000}) async* {
var total = 0;
var count = 0;
while (true) {
final points = generateRandom().take(batch);
final inside = points.where((p) => p.isInsideUnitCircle);
total += batch;
count += inside.length;
final ratio = count / total;
yield ratio * 4;
}
}
A função computePi({int batch = 100000})
, testando no DartPad, resumindo, é tipo um CodePen para Dart; Percebi que as chaves com a variável int batch
, é um objeto, que pode receber outros parâmetros, sendo pré-setado. Onde Stream<double>
, sinalizando que a função é assíncrono, que retorna ou produz, uma Stream
de números de pontos flutuantes de precisão dupla, sendo obrigatório o uso do *
quando for uma Stream
. Com algumas variáveis total
e count
, que possuem inferência de tipo, sendo int
; Com um laço de repetição infinito, sendo o while(true)
, por estar sempre como verdade, ele nunca para; É por esse motivo, quando a função computePi()
é chamada, tem que ser utilizado o método .take(100)
, para que ele possa parar na centésima repetição.
Tendo três propriedades/variáveis, points
, inside
e o ratio
. Que devem ser utilizadas, pois são do tipo final
.
-
O
points
, recebe a funçãogenerateRandom()
com o métodotake
, com a variávelbatch
; Oinside
recebe opoints
com o métodowhere
com(p) => p.isInsideUnitCircle
essa função de seta (arrow function ou callback), recebe ump
(ou um “x”), que neste caso é o retorno dopoints
ou umpoint
, todop.isInsideUnitCircle
que for verdadeiro, irá para oinside
. Onde oisInsideUnitCircle
vem de dentro daclass Point
, que ainda veremos. -
E logo abaixo temos o
total = total + batch;
outotal += batch;
que sempre adiciona o resultado detotal + batch
aototal
. -
E o
count = count + inside.length;
oucount += inside.length;
, que agora oinside.length
é o tamanho da instância/objeto, que contém as verificações, por esse motivo é usado o.length
, para pegar o tamanho e ter um número, umint
.
O ratio
sendo count / total
; final ratio = count / total;
A área de um círculo é A = π⋅r², portanto π = A/r². Então, quando os dados são pontos aleatórios com x ∈ <0,1>, y ∈ <0,1>, a razão daqueles dentro de um círculo unitário deve se aproximar de π / 4. Portanto, o valor de π deveria ser:
yield ratio * 4
;
A explicação deste cálculo está como comentário dentro do código, é possível perceber com o código completo logo acima.
// Area of a circle is A = π⋅r², therefore π = A/r².
// So, when given random points with x ∈ <0,1>,
// y ∈ <0,1>, the ratio of those inside a unit circle
// should approach π / 4. Therefore, the value of π
// should be:
O yield
adiciona um valor ao fluxo de saída do async*
da função, com um laço de repetição. É como um return
, mas não encerra a função. “Geradores”.
Iterable<Point> generateRandom([int? seed]) sync* {
final random = Random(seed);
while (true) {
yield Point(random.nextDouble(), random.nextDouble());
}
}
A função generateRandom([int? seed])
sendo sync*
, até esse exato ponto não tenho ideia da diferença entre o async*
e sync*
. Então vamos deixar a explicação da diferença para depois… Enquanto o [int? seed]
é um parâmetro que pode ou não ser passada, aparentemente é necessário a utilização de colchetes.
O Iterable<Point>
, diz que a função será um sync
e recebe o tipo da classe Point
, que por sua vez, é possível utilizar propriedades, como Point(random.nextDouble(), random.nextDouble());
, e métodos, como isInsideUnitCircle
, que neste caso, ele está dentro de cada objeto de retorno da função generateRandom()
, isso significa que você não pode usar ele assim: generateRandom().isInsideUnitCircle
; Mas pode usar dentro de um where
, por exemplo: points.where((p) => p.isInsideUnitCircle);
. Como visto anteriormente na função computePi
.
A propriedade/variável final random
recebe a função Random(seed);
com o parâmetro seed
, que pode ou não ser passada. Que foi importado inicialmente lá no topo com import 'dart:math' show Random;
e utilizado diretamente com o Random
.
Novamente temos um laço de repetição que “nunca acaba”, o while (true)
que contém o retorno da função ou yield
, que neste caso é uma constante, que define os parâmetros da class Point
.
class Point {
final double x;
final double y;
const Point(this.x, this.y);
bool get isInsideUnitCircle => x * x + y * y <= 1;
}
Uma classe pode conter, métodos e propriedades. Contendo duas propriedades final double x/y
, tendo que ser sentadas dentro da constante com o mesmo nome da classe Point
, que aparentemente parece ser um método construtor da classe, por ser uma constante, ele não possui um corpo ou as chaves {}
.
O método isInsideUnitCircle
, verifica que, x
vezes x
mais y
vezes y
é menor ou igual à 1, ele retorna um bool, verdadeiro ou falso
. Não sei por qual motivo, mas, é preciso deixar explícito que ele é um get
.
Getters e setters:
Getters e setters são métodos especiais que fornecem acesso de leitura e gravação às propriedades de um objeto. Lembre-se de que cada variável de instância tem um getter implícito, mais um setter, se apropriado. Você pode criar propriedades adicionais implementando getters e setters, usando as palavras-chave get e set.
Atenção: Gostaria de deixar bem claro, que, até esse ponto, estamos na página do overview, que não contém nada que escrevi até o momento, tudo isso eu retirei com base na minha pequena experiência com Java, Kotlin, e com JavaScript/TypeScript que atualmente é a minha linguagem principal que utilizo.
Dart: As bibliotecas:
- Tem um rico conjunto de bibliotecas principais, fornecendo o essencial para muitas tarefas diárias de programação:
dart:core, dart:collection, dart:collection, dart:math, dart:io, dart:async, dart:typed_data, dart:ffi, dart:isolate, dart:html
.
Sendo utilizados para aplicativos Flutter, web e não-web. Confira as principais bibliotecas, e o que cada item faz no Core libraries.
Dart: As plataformas:
A tecnologia do compilador do Dart permite que você execute o código de diferentes maneiras.
-
Plataforma nativa : para aplicativos direcionados a dispositivos móveis e desktop, o Dart inclui uma VM Dart com compilação just-in-time (JIT) e um compilador AOT (ahead-of-time) para produzir código de máquina.
-
Plataforma da Web : para aplicativos voltados para a Web, o Dart pode compilar para fins de desenvolvimento ou produção. Seu compilador web traduz o Dart em JavaScript.
A estrutura do Flutter é um kit de ferramentas de interface do usuário multiplataforma popular desenvolvido pela plataforma Dart e que fornece ferramentas e bibliotecas de interface do usuário para criar experiências de interface do usuário executadas em iOS, Android, macOS, Windows, Linux e na Web.
A Dart VM, oferece um compilador just-in-time (JIT) com recompilação incremental (permitindo hot reload).O compilador Dart ahead-of-time (AOT) pode compilar para ARM nativo ou código de máquina x64. O Dart Web, permite a execução do código Dart em plataformas da Web com JavaScript.
Independentemente de qual plataforma você usa ou como você compila seu código, a execução do código requer um tempo de execução do Dart. Esse tempo de execução é responsável pelas seguintes tarefas críticas:
-
Gerenciamento de memória: o Dart usa um modelo de memória gerenciada, em que a memória não utilizada é recuperada por um coletor de lixo (GC).
-
Aplicação do sistema de tipo Dart: embora a maioria das verificações de tipo no Dart sejam estáticas (tempo de compilação), algumas verificações de tipo são dinâmicas (tempo de execução). Por exemplo, o tempo de execução do Dart impõe verificações dinâmicas por verificação de tipo e operadores de conversão .
-
Gerenciando isolados : o tempo de execução do Dart controla o isolado principal (onde o código normalmente é executado) e quaisquer outros isolados criados pelo aplicativo.
Em plataformas nativas, o tempo de execução do Dart é incluído automaticamente em executáveis independentes e faz parte da VM do Dart fornecida pelo dart run comando.
Documentação do Dart
Ela é dividida em 8(oito) partes:
- Samples & tutorials (Amostras e tutoriais)
-
Contendo exemplos da linguagem, com um “Hello, World!” com print, variáveis, instruções de fluxo de controle (if, for, while…), funções, comentários(// , /* */), importações, classes, enums, herança, mixins, interfaces e classes abstratas, async, exceções.
-
Codelabs, que são como mini-cursos/tutoriais ou guias, sobre um determinado assunto, e algumas vezes criando algum aplicativo(não necessariamente com uma interface gráfica). O Google disponibiliza muitos outros Codelab.
-
Tutoriais básicos, programação assíncrona, instalação de pacotes; Usando o Dart no lado do servidor, crie aplicativos autônomos simples, que executam na VM do Dart e faça a compilação AOT para código de máquina nativo. Implementação de servidores web escritos em Dart e aplicativos web.
-
- Language (Linguagem)
- Passando por todos os aspectos da linguagem em aspectos individuais.
- Bibliotecas principais (Core libraries)
- Pacotes (Packages)
- Desenvolvimento (Development)
- Ferramentas e técnicas (Tools & techniques)
- Recursos (Resources)
-Livros(pagos), código de conduta, vídeos, perguntas frequentes e guia para usar DartPads. - Sites relacionados (Related sites)
Com Certeza, Conteúdo não falta
A tour of the Dart language
Observação: Essa parte irá utilizar muito da documentação, se você não tiver nem um problema com a documentação em inglês, recomendo fortemente que leia ela diretamente, A tour of the Dart language.
Esta parte mostra como usar cada recurso principal do Dart, de variáveis e operadores a classes e bibliotecas, supondo que você já saiba programar em outra linguagem. Para uma introdução mais breve e menos completa ao idioma, consulte a página de exemplos de idiomas.
Para saber mais sobre as principais bibliotecas do Dart, consulte o tour da biblioteca. Sempre que desejar mais detalhes sobre um recurso de idioma, consulte a especificação de idioma do Dart.
Variáveis
var name = 'Bob';
Variáveis armazenam referências. A variável chamada nome
contém uma referência a um String objeto com valor Bob
.
A variável name
é inferido como String
, mas você pode alterar esse tipo, especificando-o. Se um objeto não estiver restrito a um único tipo, especifique como Object (ou dynamic se necessário).
Object name = 'Bob';
Outra opção é declarar explicitamente o tipo que seria inferido:
String name = 'Bob';
Valor padrão
As variáveis não inicializadas que têm um tipo anulável têm um valor inicial de null. (Se você não optou pela segurança nula , todas as variáveis têm um tipo anulável.) Mesmo as variáveis com tipos numéricos são inicialmente nulas, porque os números — como tudo mais no Dart — são objetos.
int? lineCount;
assert(lineCount == null);
Observação: O código de produção ignora a chamada assert(). Durante o desenvolvimento, por outro lado, assert(condição) lança uma exceção se a condição for falsa.
Se você ativar a segurança nula, deverá inicializar os valores das variáveis não anuláveis antes de usá-las:
int lineCount = 0;
Você não precisa inicializar uma variável local onde ela é declarada, mas precisa atribuir um valor a ela antes de usá-la. Por exemplo, o código a seguir é válido porque o Dart pode detectar que lineCount
não é nulo no momento em que é passado para print()
:
int lineCount;
if (weLikeToCount) {
lineCount = countLines();
} else {
lineCount = 0;
}
print(lineCount);
As variáveis de nível superior e de classe são inicializadas lentamente; O código de inicialização é executado na primeira vez que a variável é usada.
Continua No Próximo Episódio...
Fonte: https://dart.dev/