[Pelas docas] Flutter. Parte 2. Para desenvolvedores do iOS

Após um longo intervalo, continuarei a falar sobre a estrutura popular do Flutter em um formato de perguntas e respostas. Você pode encontrar o primeiro artigo para desenvolvedores do Android aqui , e hoje haverá material útil para desenvolvedores para iOS.

Se você tiver pouco tempo para um estudo independente e aprofundado da documentação, mas deseja entender o que o Flutter é bom e como usá-lo, consulte o gato.



Flutter. Parte 1. Para desenvolvedores do Android
Flutter. Parte 2. Para desenvolvedores do iOS
Flutter. Parte 3. Para reagir a desenvolvedores nativos
Flutter. Parte 4. Para desenvolvedores da web
Flutter. Parte 5. Para desenvolvedores do Xamarin.Forms

Conteúdo:


  1. Visualizações

  2. Navegação

  3. Rosqueamento e assincronicidade

  4. Estrutura e Recursos do Projeto

  5. ViewControllers

  6. Layouts

  7. Gestos e manipulação de eventos por toque

  8. Estilo de aplicativo

  9. Formulário de entrada

  10. Flutter Plugins

  11. Bancos de dados e armazenamento local

  12. Notificações



Visualizações


Pergunta:


O que é o analógico UIView no Flutter?

A resposta é:


Widget

Diferenças:


UIView é realmente o que estará na tela. SetNeedsDisplay () é chamado para exibir as alterações.

Widget - uma descrição do que estará na tela. Pois a mudança é criada novamente.

Informações adicionais:


O Flutter inclui a biblioteca Cupertino Widgets . Ele contém widgets que implementam as diretrizes de design da Apple .

Pergunta:


Como atualizar a exibição de widgets?

A resposta é:


Usando StatefulWidget e seu estado . O Flutter possui 2 tipos de widgets: StatelessWidget e StatefulWidget . Eles funcionam da mesma maneira, a única diferença está no estado de renderização.

Diferenças:


StatelessWidget tem um estado imutável. Adequado para a exibição de texto, logotipo etc. I.e. se o elemento na tela não mudar durante todo o tempo de exibição, ele combina com você. Também pode ser usado como um contêiner para widgets com estado.

StatefulWidget possui o estado State, que armazena informações sobre o estado atual. Se você deseja alterar um elemento na tela ao executar alguma ação (uma resposta veio do servidor, o usuário clicou em um botão etc.) - esta é sua opção.

Um exemplo:


1) StatelessWidget - Texto

Text( 'I like Flutter!', style: TextStyle(fontWeight: FontWeight.bold), ); 

2) StatefulWidget - quando você clica no botão (FloatingActionButton), o texto no widget Texto muda de Eu gosto de flutter para Flutter é impressionante!

 import 'package:flutter/material.dart'; void main() { runApp(SampleApp()); } class SampleApp extends StatelessWidget { //     . @override Widget build(BuildContext context) { return MaterialApp( title: 'Sample App', theme: ThemeData( primarySwatch: Colors.blue, ), home: SampleAppPage(), ); } } class SampleAppPage extends StatefulWidget { SampleAppPage({Key key}) : super(key: key); @override _SampleAppPageState createState() => _SampleAppPageState(); } class _SampleAppPageState extends State<SampleAppPage> { //   String textToShow = "  Flutter"; void _updateText() { setState(() { //   textToShow = "Flutter !"; }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Sample App"), ), body: Center(child: Text(textToShow)), floatingActionButton: FloatingActionButton( onPressed: _updateText, tooltip: ' ', child: Icon(Icons.update), ), ); } } 

Pergunta:


Como fazer o layout de uma tela com widgets? Onde está o storyboard ?

A resposta é:


Flutter não tem Storyboard . Tudo digitado na árvore de widgets diretamente no código.

Um exemplo:


 @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Sample App"), ), body: Center( child: CupertinoButton( onPressed: () { setState(() { _pressedCount += 1; }); }, child: Text('Hello'), padding: EdgeInsets.only(left: 10.0, right: 10.0), ), ), ); } 

Todos os widgets padrão no Flutter podem ser visualizados no catálogo de widgets .

Pergunta:


Como adicionar ou remover um componente no layout enquanto o aplicativo está em execução?

A resposta é:


Através de uma função que retornará o widget desejado, dependendo do estado.

Diferenças:


No iOS, você pode adicionar addview () ou remover fromSuperview (). No Flutter, você não pode os widgets permanecem inalterados. Somente sua condição pode mudar.

Um exemplo:


Mude o texto para o botão clicando em FloatingActionButton.

 class SampleApp extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: 'Sample App', theme: ThemeData( primarySwatch: Colors.blue, ), home: SampleAppPage(), ); } } class SampleAppPage extends StatefulWidget { SampleAppPage({Key key}) : super(key: key); @override _SampleAppPageState createState() => _SampleAppPageState(); } class _SampleAppPageState extends State<SampleAppPage> { // Default value for toggle bool toggle = true; void _toggle() { setState(() { toggle = !toggle; }); } _getToggleChild() { if (toggle) { return Text('Toggle One'); } else { return CupertinoButton( onPressed: () {}, child: Text('Toggle Two'), ); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Sample App"), ), body: Center( child: _getToggleChild(), ), floatingActionButton: FloatingActionButton( onPressed: _toggle, tooltip: 'Update Text', child: Icon(Icons.update), ), ); } } 

Pergunta:


Como animar widgets?

A resposta é:


Usando a classe AnimationController , que é o descendente da classe abstrata Animation <T> . Além de iniciar a animação, ele pode pausar, retroceder, parar e reproduzir na direção oposta. Funciona com o Ticker , que relata um redesenho da tela.

Diferenças:


No iOS, você pode animar uma exibição usando animar (com Duração: animações :). No Flutter, a animação deve ser escrita em código usando o AnimationController.

Informações adicionais:


Você pode aprender mais nos widgets Animação e movimento , tutorial Animações e visão geral de Animações .

Um exemplo:


Desvanece a animação do logotipo Flutter.

 class SampleApp extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: 'Fade Demo', theme: ThemeData( primarySwatch: Colors.blue, ), home: MyFadeTest(title: 'Fade Demo'), ); } } class MyFadeTest extends StatefulWidget { MyFadeTest({Key key, this.title}) : super(key: key); final String title; @override _MyFadeTest createState() => _MyFadeTest(); } class _MyFadeTest extends State<MyFadeTest> with TickerProviderStateMixin { AnimationController controller; CurvedAnimation curve; @override void initState() { controller = AnimationController(duration: const Duration(milliseconds: 2000), vsync: this); curve = CurvedAnimation(parent: controller, curve: Curves.easeIn); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: Center( child: Container( child: FadeTransition( opacity: curve, child: FlutterLogo( size: 100.0, ) ) ) ), floatingActionButton: FloatingActionButton( tooltip: 'Fade', child: Icon(Icons.brush), onPressed: () { controller.forward(); }, ), ); } @override dispose() { controller.dispose(); super.dispose(); } } 

Pergunta:


Como usar o CoreGraphics ?

A resposta é:


O Flutter usa a API do Canvas no mecanismo de baixo nível do Skia, em vez do CoreGraphics. O Android usa uma API Canvas semelhante.

Informações adicionais:


O Flutter possui duas classes para desenhar no Canvas: CustomPaint e CustomPainter . O segundo implementa seu algoritmo de renderização.

Leia mais aqui: StackOverflow

Um exemplo:


 class SignaturePainter extends CustomPainter { SignaturePainter(this.points); final List<Offset> points; void paint(Canvas canvas, Size size) { var paint = Paint() ..color = Colors.black ..strokeCap = StrokeCap.round ..strokeWidth = 5.0; for (int i = 0; i < points.length - 1; i++) { if (points[i] != null && points[i + 1] != null) canvas.drawLine(points[i], points[i + 1], paint); } } bool shouldRepaint(SignaturePainter other) => other.points != points; } class Signature extends StatefulWidget { SignatureState createState() => SignatureState(); } class SignatureState extends State<Signature> { List<Offset> _points = <Offset>[]; Widget build(BuildContext context) { return GestureDetector( onPanUpdate: (DragUpdateDetails details) { setState(() { RenderBox referenceBox = context.findRenderObject(); Offset localPosition = referenceBox.globalToLocal(details.globalPosition); _points = List.from(_points)..add(localPosition); }); }, onPanEnd: (DragEndDetails details) => _points.add(null), child: CustomPaint(painter: SignaturePainter(_points), size: Size.infinite), ); } } 

Pergunta:


Como mudar a transparência dos widgets?

A resposta é:


Quebra no widget Opacidade .

Diferenças:


No iOS, todas as visualizações têm .opacity ou .alpha. No Flutter, essa opção substitui o widget do wrapper.

Pergunta:


Como criar widgets personalizados?

A resposta é:


Componha widgets dentro de um (em vez de herança).

Diferenças:


No iOS, você pode herdar da exibição que nos interessa e adicionar sua própria lógica. No Flutter, um widget é sempre herdado de StatelessWidget ou StatefulWidget. I.e. você precisa criar um novo widget e usá-lo como conjunto de parâmetros ou campos.

Um exemplo:


 class CustomButton extends StatelessWidget { final String label; CustomButton(this.label); @override Widget build(BuildContext context) { return RaisedButton(onPressed: () {}, child: Text(label)); } } @override Widget build(BuildContext context) { return Center( child: CustomButton("Hello"), ); } 

Navegação


Pergunta:


Como implementar a navegação entre telas no Flutter?

A resposta é:


A navegação entre telas usa as classes Navigator e Route .

Diferenças:


O Flutter não possui conceitos como UIViewController e UINavigationController. Existem Navegador (Navegador) e Rotas (rotas). O Navegador é semelhante ao UINavigationController em princípio. Pode pressionar push () ou pop () para a rota que você especificar. Route é um tipo de UIViewController, mas no Flutter é comum comparar com uma tela ou página.

O Flutter possui dois métodos de navegação:

  • Descrever um mapa com nomes de rotas
  • Navegue diretamente para a rota.

Um exemplo:


 void main() { runApp(CupertinoApp( home: MyAppHome(), // becomes the route named '/' routes: <String, WidgetBuilder> { '/a': (BuildContext context) => MyPage(title: 'page A'), '/b': (BuildContext context) => MyPage(title: 'page B'), '/c': (BuildContext context) => MyPage(title: 'page C'), }, )); } Navigator.of(context).pushNamed('/b'); 

Pergunta:


Como navegar para um aplicativo de terceiros?

A resposta é:


Interagindo com a camada iOS do aplicativo por meio do MethodChannel ou usando o plug-in do iniciador de URL .

Pergunta:


Como voltar ao iOS no ViewController?

A resposta é:


Chamando SystemNavigator.pop ().

Informações adicionais:


SystemNavigator.pop () do código Dart chama o seguinte código no iOS:
 UIViewController* viewController = [UIApplication sharedApplication].keyWindow.rootViewController; if ([viewController isKindOfClass:[UINavigationController class]]) { [((UINavigationController*)viewController) popViewControllerAnimated:NO]; } 

Se não é isso que você precisa, você pode fazer sua implementação através do MethodChannel .

Rosqueamento e assincronicidade


Pergunta:


Como escrever código assíncrono no Flutter?

A resposta é:


O Dart implementa um modelo de execução de thread único que é executado no Isolates . A execução assíncrona usa async / waitit, com a qual você pode estar familiarizado com as rotinas C #, JavaScript ou Kotlin.

Um exemplo:


Atendendo à solicitação e retornando o resultado para atualizar a interface do usuário:

 loadData() async { String dataURL = "https://jsonplaceholder.typicode.com/posts"; http.Response response = await http.get(dataURL); setState(() { widgets = json.decode(response.body); }); } 

Quando a resposta à solicitação é recebida, você precisa chamar o método setState () para redesenhar a árvore do widget com os novos dados.

Um exemplo:


Carregando e atualizando dados em um ListView :

 import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; void main() { runApp(SampleApp()); } class SampleApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Sample App', theme: ThemeData( primarySwatch: Colors.blue, ), home: SampleAppPage(), ); } } class SampleAppPage extends StatefulWidget { SampleAppPage({Key key}) : super(key: key); @override _SampleAppPageState createState() => _SampleAppPageState(); } class _SampleAppPageState extends State<SampleAppPage> { List widgets = []; @override void initState() { super.initState(); loadData(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Sample App"), ), body: ListView.builder( itemCount: widgets.length, itemBuilder: (BuildContext context, int position) { return getRow(position); })); } Widget getRow(int i) { return Padding( padding: EdgeInsets.all(10.0), child: Text("Row ${widgets[i]["title"]}") ); } loadData() async { String dataURL = "https://jsonplaceholder.typicode.com/posts"; http.Response response = await http.get(dataURL); setState(() { widgets = json.decode(response.body); }); } } 

Pergunta:


Como executar código em um thread em segundo plano?

A resposta é:


Como mencionado acima - usando assíncrono / espera e isolamento (Isolar).

Diferenças:


Pronto para uso no iOS, você pode usar o Operation com uma possível redefinição de métodos. No Flutter "pronto para uso", você só precisa usar assíncrono / aguardar, o Dart cuidará do resto.

Um exemplo:


Aqui o método dataLoader () é isolado. Isoladamente, você pode executar operações pesadas, como analisar JSONs grandes, criptografia, processamento de imagem etc.

 loadData() async { ReceivePort receivePort = ReceivePort(); await Isolate.spawn(dataLoader, receivePort.sendPort); // The 'echo' isolate sends its SendPort as the first message SendPort sendPort = await receivePort.first; List msg = await sendReceive(sendPort, "https://jsonplaceholder.typicode.com/posts"); setState(() { widgets = msg; }); } // The entry point for the isolate static dataLoader(SendPort sendPort) async { // Open the ReceivePort for incoming messages. ReceivePort port = ReceivePort(); // Notify any other isolates what port this isolate listens to. sendPort.send(port.sendPort); await for (var msg in port) { String data = msg[0]; SendPort replyTo = msg[1]; String dataURL = data; http.Response response = await http.get(dataURL); // Lots of JSON to parse replyTo.send(json.decode(response.body)); } } Future sendReceive(SendPort port, msg) { ReceivePort response = ReceivePort(); port.send([msg, response.sendPort]); return response.first; }   : import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import 'dart:async'; import 'dart:isolate'; void main() { runApp(SampleApp()); } class SampleApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Sample App', theme: ThemeData( primarySwatch: Colors.blue, ), home: SampleAppPage(), ); } } class SampleAppPage extends StatefulWidget { SampleAppPage({Key key}) : super(key: key); @override _SampleAppPageState createState() => _SampleAppPageState(); } class _SampleAppPageState extends State<SampleAppPage> { List widgets = []; @override void initState() { super.initState(); loadData(); } showLoadingDialog() { if (widgets.length == 0) { return true; } return false; } getBody() { if (showLoadingDialog()) { return getProgressDialog(); } else { return getListView(); } } getProgressDialog() { return Center(child: CircularProgressIndicator()); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Sample App"), ), body: getBody()); } ListView getListView() => ListView.builder( itemCount: widgets.length, itemBuilder: (BuildContext context, int position) { return getRow(position); }); Widget getRow(int i) { return Padding(padding: EdgeInsets.all(10.0), child: Text("Row ${widgets[i]["title"]}")); } loadData() async { ReceivePort receivePort = ReceivePort(); await Isolate.spawn(dataLoader, receivePort.sendPort); // The 'echo' isolate sends its SendPort as the first message SendPort sendPort = await receivePort.first; List msg = await sendReceive(sendPort, "https://jsonplaceholder.typicode.com/posts"); setState(() { widgets = msg; }); } // the entry point for the isolate static dataLoader(SendPort sendPort) async { // Open the ReceivePort for incoming messages. ReceivePort port = ReceivePort(); // Notify any other isolates what port this isolate listens to. sendPort.send(port.sendPort); await for (var msg in port) { String data = msg[0]; SendPort replyTo = msg[1]; String dataURL = data; http.Response response = await http.get(dataURL); // Lots of JSON to parse replyTo.send(json.decode(response.body)); } } Future sendReceive(SendPort port, msg) { ReceivePort response = ReceivePort(); port.send([msg, response.sendPort]); return response.first; } } 

Pergunta:


Como fazer solicitações de rede no Flutter?

A resposta é:


O Flutter possui seu próprio pacote HTTP .

Um exemplo:


Para usar o pacote HTTP, adicione-o como uma dependência no pubspec.yaml:

 dependencies: ... http: ^0.11.3+16 

Para executar a solicitação, aguarde a chamada na função assíncrona http.get ():

 import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; [...] loadData() async { String dataURL = "https://jsonplaceholder.typicode.com/posts"; http.Response response = await http.get(dataURL); setState(() { widgets = json.decode(response.body); }); } } 

Pergunta:


Como mostrar progresso?

A resposta é:


Usando o widget ProgressIndicator .

Um exemplo:


 import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; void main() { runApp(SampleApp()); } class SampleApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Sample App', theme: ThemeData( primarySwatch: Colors.blue, ), home: SampleAppPage(), ); } } class SampleAppPage extends StatefulWidget { SampleAppPage({Key key}) : super(key: key); @override _SampleAppPageState createState() => _SampleAppPageState(); } class _SampleAppPageState extends State<SampleAppPage> { List widgets = []; @override void initState() { super.initState(); loadData(); } showLoadingDialog() { return widgets.length == 0; } getBody() { if (showLoadingDialog()) { return getProgressDialog(); } else { return getListView(); } } getProgressDialog() { return Center(child: CircularProgressIndicator()); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Sample App"), ), body: getBody()); } ListView getListView() => ListView.builder( itemCount: widgets.length, itemBuilder: (BuildContext context, int position) { return getRow(position); }); Widget getRow(int i) { return Padding(padding: EdgeInsets.all(10.0), child: Text("Row ${widgets[i]["title"]}")); } loadData() async { String dataURL = "https://jsonplaceholder.typicode.com/posts"; http.Response response = await http.get(dataURL); setState(() { widgets = json.decode(response.body); }); } } 

Estrutura e Recursos do Projeto


Pergunta:


Onde armazenar recursos de diferentes resoluções?

A resposta é:


Em ativos.

Diferenças:


No iOS, os recursos gráficos têm o Images.xcasset, localizado na pasta de ativos. Flutter tem apenas ativos. A pasta do recurso pode estar localizada em qualquer lugar do projeto, e o mais importante, escreva o caminho para ele no arquivo pubspec.yaml.

Informações adicionais:


Os tamanhos dos recursos gráficos no iOS e no Flutter são idênticos e seguem o formato baseado em densidade.

Local do Recurso:

 images/my_icon.png // Base: 1.0x image images/2.0x/my_icon.png // 2.0x image images/3.0x/my_icon.png // 3.0x image 

Caminho no arquivo pubspec.yaml:

 assets: - images/my_icon.png 

Usando AssetImage :

 return AssetImage("images/a_dot_burr.jpeg"); 

Usando o ativo diretamente:

 @override Widget build(BuildContext context) { return Image.asset("images/my_image.png"); } 

Pergunta:


Onde armazenar cordas? Como localizá-los?

A resposta é:


Armazenar em campos estáticos. Localize usando o pacote intl .

Um exemplo:


 class Strings { static String welcomeMessage = "Welcome To Flutter"; } Text(Strings.welcomeMessage) 

Pergunta:


Qual é a contrapartida do CocoaPods? Como adicionar dependências?

A resposta é:


pubspec.yaml.

Informações adicionais:


O Flutter delega a construção para construtores nativos do Android e iOS. Veja uma lista de todas as bibliotecas populares de Flutter no Pub .

ViewControllers


Pergunta:


Qual é o equivalente do ViewController no Flutter?

A resposta é:


Tudo no Flutter é widgets. O papel do ViewController para trabalhar com a interface do usuário é desempenhado por widgets. E o papel da navegação, conforme mencionado no parágrafo sobre navegação, é Navegador e Rota.

Pergunta:


Como lidar com eventos do ciclo de vida?

A resposta é:


Usando o método WidgetsBinding e didChangeAppLifecycleState () .

Informações adicionais:


O Flutter usa FlutterAppDelegate no código nativo, e o mecanismo Flutter torna o tratamento das alterações de estado o mais discreto possível. Mas se você ainda precisa fazer algum trabalho, dependendo do estado, o ciclo de vida é um pouco diferente:

  • inativo - o aplicativo está inativo e não recebe entrada do usuário. Este estado é apenas no iOS, no Android não há analógico;
  • pausado - o aplicativo está atualmente invisível para o usuário, não responde à entrada do usuário, mas funciona em segundo plano;
  • retomado - o aplicativo está visível e responde à entrada do usuário;
  • suspensão - aplicação no processo de parada. Este estado é apenas no Android, no iOS não há analógico.

Isso é descrito em mais detalhes na documentação do AppLifecycleStatus .

Um exemplo:


 import 'package:flutter/widgets.dart'; class LifecycleWatcher extends StatefulWidget { @override _LifecycleWatcherState createState() => _LifecycleWatcherState(); } class _LifecycleWatcherState extends State<LifecycleWatcher> with WidgetsBindingObserver { AppLifecycleState _lastLifecycleState; @override void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); } @override void dispose() { WidgetsBinding.instance.removeObserver(this); super.dispose(); } @override void didChangeAppLifecycleState(AppLifecycleState state) { setState(() { _lastLifecycleState = state; }); } @override Widget build(BuildContext context) { if (_lastLifecycleState == null) return Text('This widget has not observed any lifecycle changes.', textDirection: TextDirection.ltr); return Text('The most recent lifecycle state this widget observed was: $_lastLifecycleState.', textDirection: TextDirection.ltr); } } void main() { runApp(Center(child: LifecycleWatcher())); } 

Layouts


Pergunta:


Qual é o equivalente de UITableView e UICollectionView ?

A resposta é:


ListView

Um exemplo:


 import 'package:flutter/material.dart'; void main() { runApp(SampleApp()); } class SampleApp extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: 'Sample App', theme: ThemeData( primarySwatch: Colors.blue, ), home: SampleAppPage(), ); } } class SampleAppPage extends StatefulWidget { SampleAppPage({Key key}) : super(key: key); @override _SampleAppPageState createState() => _SampleAppPageState(); } class _SampleAppPageState extends State<SampleAppPage> { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Sample App"), ), body: ListView(children: _getListData()), ); } _getListData() { List<Widget> widgets = []; for (int i = 0; i < 100; i++) { widgets.add(Padding(padding: EdgeInsets.all(10.0), child: Text("Row $i"))); } return widgets; } } 

Pergunta:


Como sei em qual item da lista foi clicado?

A resposta é:


O widget, que é um elemento da lista, deve manipular o clique nele.

Diferenças:


No iOS, o método tableView: didSelectRowAtIndexPath: é o responsável por isso. No Flutter, o item da lista deve ser agrupado em um widget que lida com cliques, como o GestureDetector .

Pergunta:


Como atualizar dinamicamente um ListView ?

A resposta é:


Atualize a lista de dados e chame setState ().

Diferenças:


No iOS, você precisa atualizar os dados e chamar o método reloadData. No Flutter, após setState (), o widget será redesenhado novamente.

Um exemplo:


 import 'package:flutter/material.dart'; void main() { runApp(SampleApp()); } class SampleApp extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: 'Sample App', theme: ThemeData( primarySwatch: Colors.blue, ), home: SampleAppPage(), ); } } class SampleAppPage extends StatefulWidget { SampleAppPage({Key key}) : super(key: key); @override _SampleAppPageState createState() => _SampleAppPageState(); } class _SampleAppPageState extends State<SampleAppPage> { List widgets = []; @override void initState() { super.initState(); for (int i = 0; i < 100; i++) { widgets.add(getRow(i)); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Sample App"), ), body: ListView(children: widgets), ); } Widget getRow(int i) { return GestureDetector( child: Padding( padding: EdgeInsets.all(10.0), child: Text("Row $i"), ), onTap: () { setState(() { widgets = List.from(widgets); widgets.add(getRow(widgets.length + 1)); print('row $i'); }); }, ); } } 


Informações adicionais:


É recomendável usar o ListView.Builder para criar uma lista.

Um exemplo:


 import 'package:flutter/material.dart'; void main() { runApp(SampleApp()); } class SampleApp extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: 'Sample App', theme: ThemeData( primarySwatch: Colors.blue, ), home: SampleAppPage(), ); } } class SampleAppPage extends StatefulWidget { SampleAppPage({Key key}) : super(key: key); @override _SampleAppPageState createState() => _SampleAppPageState(); } class _SampleAppPageState extends State<SampleAppPage> { List widgets = []; @override void initState() { super.initState(); for (int i = 0; i < 100; i++) { widgets.add(getRow(i)); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Sample App"), ), body: ListView.builder( itemCount: widgets.length, itemBuilder: (BuildContext context, int position) { return getRow(position); }, ), ); } Widget getRow(int i) { return GestureDetector( child: Padding( padding: EdgeInsets.all(10.0), child: Text("Row $i"), ), onTap: () { setState(() { widgets.add(getRow(widgets.length + 1)); print('row $i'); }); }, ); } } 


Pergunta:


Qual é o análogo do UIScrollView ?

A resposta é:


ListView com widgets.

Um exemplo:


 @override Widget build(BuildContext context) { return ListView( children: <Widget>[ Text('Row One'), Text('Row Two'), Text('Row Three'), Text('Row Four'), ], ); } 


Informações adicionais:


Mais detalhes aqui .

Gestos e manipulação de eventos por toque


Pergunta:


Como adicionar um ouvinte onClick para widget no Flutter?

A resposta é:


Se o widget suportar cliques, em onPressed (). Caso contrário, em onTap ().

Um exemplo:


Em onPressed ():

 @override Widget build(BuildContext context) { return RaisedButton( onPressed: () { print("click"); }, child: Text("Button"), ); } 

Em onTap ():

 class SampleApp extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( body: Center( child: GestureDetector( child: FlutterLogo( size: 200.0, ), onTap: () { print("tap"); }, ), ), ); } } 

Pergunta:


Como lidar com outros gestos em widgets?

A resposta é:


Usando o GestureDetector . Eles podem lidar com as seguintes ações:

Toque



Toque duas vezes



Pressão longa



Arrasto vertical



Arrasto horizontal



Um exemplo:


Processando onDoubleTap:

 AnimationController controller; CurvedAnimation curve; @override void initState() { controller = AnimationController(duration: const Duration(milliseconds: 2000), vsync: this); curve = CurvedAnimation(parent: controller, curve: Curves.easeIn); } class SampleApp extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( body: Center( child: GestureDetector( child: RotationTransition( turns: curve, child: FlutterLogo( size: 200.0, )), onDoubleTap: () { if (controller.isCompleted) { controller.reverse(); } else { controller.forward(); } }, ), ), ); } } 


Estilo de aplicativo


Pergunta:


Como usar o tema (Tema) no aplicativo?

A resposta é:


Usando o widget MaterialApp ou WidgetApp como raiz no aplicativo.

Um exemplo:


 class SampleApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Sample App', theme: ThemeData( primarySwatch: Colors.blue, textSelectionColor: Colors.red ), home: SampleAppPage(), ); } } 


Pergunta:


Como usar fontes personalizadas?

A resposta é:


Você só precisa colocar o arquivo de fonte na pasta (pense no nome você mesmo) e indicar o caminho para ele em pubspec.yaml.

Um exemplo:


 fonts: - family: MyCustomFont fonts: - asset: fonts/MyCustomFont.ttf - style: italic 

 @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Sample App"), ), body: Center( child: Text( 'This is a custom font text', style: TextStyle(fontFamily: 'MyCustomFont'), ), ), ); } 

Pergunta:


Como estilizar widgets de texto?

A resposta é:


Usando parâmetros:

  • cor;
  • decoração;
  • decorationColor;
  • decorationStyle;
  • fontFamily;
  • fontSize;
  • fontStyle;
  • fontWeight;
  • hashCode;
  • altura;
  • herdar;
  • letterSpacing;
  • textBaseline;
  • wordSpacing.


Formulário de entrada


Pergunta:


Como obter o resultado da entrada do usuário?

A resposta é:


Usando TextEditingController .

Um exemplo:

 class _MyFormState extends State<MyForm> { // Create a text controller and use it to retrieve the current value. // of the TextField! final myController = TextEditingController(); @override void dispose() { // Clean up the controller when disposing of the Widget. myController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Retrieve Text Input'), ), body: Padding( padding: const EdgeInsets.all(16.0), child: TextField( controller: myController, ), ), floatingActionButton: FloatingActionButton( // When the user presses the button, show an alert dialog with the // text the user has typed into our text field. onPressed: () { return showDialog( context: context, builder: (context) { return AlertDialog( // Retrieve the text the user has typed in using our // TextEditingController content: Text(myController.text), ); }, ); }, tooltip: 'Show me the value!', child: Icon(Icons.text_fields), ), ); } } 


: Retrieve the value of a text field .

:


hint TextInput ?

A resposta é:


InputDecoration , .

Um exemplo:


 body: Center( child: TextField( decoration: InputDecoration(hintText: "This is a hint"), ), ) 

:


?

A resposta é:


InputDecoration .

Um exemplo:


 class SampleApp extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: 'Sample App', theme: ThemeData( primarySwatch: Colors.blue, ), home: SampleAppPage(), ); } } class SampleAppPage extends StatefulWidget { SampleAppPage({Key key}) : super(key: key); @override _SampleAppPageState createState() => _SampleAppPageState(); } class _SampleAppPageState extends State<SampleAppPage> { String _errorText; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Sample App"), ), body: Center( child: TextField( onSubmitted: (String text) { setState(() { if (!isEmail(text)) { _errorText = 'Error: This is not an email'; } else { _errorText = null; } }); }, decoration: InputDecoration(hintText: "This is a hint", errorText: _getErrorText()), ), ), ); } _getErrorText() { return _errorText; } bool isEmail(String emailString) { String emailRegexp = r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$'; RegExp regExp = RegExp(emailRegexp); return regExp.hasMatch(emailString); } } 

Flutter


:


GPS?

A resposta é:


geolocator .

:


?

A resposta é:


image_picker .

:


Facebook?

A resposta é:


flutter_facebook_login .

:


Firebase?

A resposta é:


Firebase Flutter first party plugins :


:


() ?

A resposta é:


Flutter EventBus . : developing packages and plugins .


:


UserDefault?

A resposta é:


Shared_Preferences plugin ( Shared Preferences Android ).

Um exemplo:


 import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; void main() { runApp( MaterialApp( home: Scaffold( body: Center( child: RaisedButton( onPressed: _incrementCounter, child: Text('Increment Counter'), ), ), ), ), ); } _incrementCounter() async { SharedPreferences prefs = await SharedPreferences.getInstance(); int counter = (prefs.getInt('counter') ?? 0) + 1; print('Pressed $counter times.'); prefs.setInt('counter', counter); } 

:


Core Data ?

A resposta é:


SQFlite .


:


push-?

A resposta é:


Firebase_Messaging .

Conclusão


. , , . « » . «-» . , ? 2016 Kotlin, - 2017. , , . , .
2016 Flutter Dart. , 2018 . . ! , , , . ( Google Fuchsia , , , Flutter ). — ! , — . . Apple Store!

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


All Articles