[Por los muelles] Aleteo. Parte 1. Para desarrolladores de Android

Flutter ya ha escrito muchos art铆culos. Cada mes se est谩 volviendo m谩s popular. As铆 que decid铆 interpretar la documentaci贸n oficial de Flutter en un formato conciso de preguntas y respuestas. Creo que muchos, como yo, no tienen suficiente tiempo libre para estudiar en detalle la documentaci贸n del marco con el que a煤n no funcionan.

Si desea comprender para qu茅 sirve este marco y evaluar cu谩nto esfuerzo tendr谩 que hacer para usarlo, bienvenido a cat.



Contenido:


  1. Vistas

  2. Intenciones

  3. IU as铆ncrona

  4. Estructura del proyecto y recursos

  5. Actividades y fragmentos

  6. Dise帽os

  7. Gestos y manejo de eventos t谩ctiles.

  8. Vistas de lista y adaptadores

  9. Trabajar con texto

  10. Formulario de entrada

  11. Flutter Plugins

  12. Temas

  13. Bases de datos y almacenamiento local.

  14. Notificaciones


Vistas


Pregunta:


驴Cu谩l es el equivalente de Ver en Flutter?

La respuesta es:


Widget

Diferencias:


Ver: de hecho, lo que estar谩 en la pantalla. Invalidate () se llama para mostrar los cambios.

Widget: una descripci贸n de lo que aparecer谩 en la pantalla. Para el cambio se crea de nuevo.

Informacion adicional:


Cuando se inicia en Android, View est谩 bajo el cap贸 de Widget. Flutter incluye la biblioteca de componentes de material . Contiene widgets que implementan pautas de dise帽o de materiales .

Pregunta:


驴C贸mo actualizar la visualizaci贸n de widgets?

La respuesta es:


Usando StatefulWidget y su estado . Flutter tiene 2 tipos de widgets: StatelessWidget y StatefulWidget . Funcionan de la misma manera, la 煤nica diferencia est谩 en el estado de representaci贸n.

Diferencias:


StatelessWidget tiene un estado inmutable. Adecuado para mostrar texto, logotipo, etc. Es decir Si el elemento en la pantalla no debe cambiar durante todo el tiempo de visualizaci贸n, entonces le conviene. Tambi茅n se puede usar como contenedor para widgets con estado.

StatefulWidget tiene el estado State, que almacena informaci贸n sobre el estado actual. Si desea cambiar un elemento en la pantalla al realizar alguna acci贸n (una respuesta vino del servidor, el usuario hizo clic en un bot贸n, etc.), esta es su opci贸n.

Un ejemplo:


1) StatelessWidget - Texto

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

2) StatefulWidget: cuando hace clic en el bot贸n (FloatingActionButton), el texto en el widget de texto cambia de "Me gusta Flutter" a "Flutter is Awesome!".

 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), ), ); } } 

Pregunta:


驴C贸mo dise帽ar una pantalla con widgets? 驴D贸nde est谩 el archivo de dise帽o XML?

La respuesta es:


Flutter no tiene dise帽o XML para pantallas. Todo est谩 compuesto en el 谩rbol de widgets directamente en el c贸digo.

Un ejemplo:


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

Todos los widgets predeterminados en Flutter se pueden ver en el cat谩logo de widgets .

Pregunta:


驴C贸mo agregar o eliminar un componente en el dise帽o mientras se ejecuta la aplicaci贸n?

La respuesta es:


A trav茅s de una funci贸n que devolver谩 el widget deseado seg煤n el estado.

Diferencias:


En Android, puede hacer addView () o removeView () en un ViewGroup. En Flutter no es posible, porque los widgets no cambian. Solo su condici贸n puede cambiar.

Un ejemplo:


C贸mo cambiar Texto a Bot贸n haciendo clic en FloatingActionButton.

 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> { //     bool toggle = true; void _toggle() { setState(() { toggle = !toggle; }); } _getToggleChild() { if (toggle) { return Text('Toggle One'); } else { return MaterialButton(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), ), ); } } 

Pregunta:


驴C贸mo animar widgets?

La respuesta es:


Usando la clase AnimationController , que es el descendiente de la clase abstracta Animation <T> . Adem谩s de comenzar la animaci贸n, puede pausarla, rebobinarla, detenerla y reproducirla en la direcci贸n opuesta. Funciona con Ticker , que informa sobre un nuevo dibujo de la pantalla.

Diferencias:


En Android, puede crear animaciones en XML o animar la vista usando animate (). En Flutter, la animaci贸n debe escribirse en c贸digo usando AnimationController.

Informacion adicional:


Puede obtener m谩s informaci贸n en los widgets de animaci贸n y movimiento , tutorial de animaciones y descripci贸n general de animaciones .

Un ejemplo:


Animaci贸n desvanecida del logotipo de Flutter.

 import 'package:flutter/material.dart'; void main() { runApp(FadeAppTest()); } class FadeAppTest 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() { super.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(); }, ), ); } } 

Pregunta:


驴C贸mo usar Canvas ?

La respuesta es:


Android y Flutter tienen la misma API para Canvas, ya que usan el mismo motor Skia de bajo nivel.

Diferencias:


No

Informacion adicional:


Flutter tiene dos clases para dibujar en Canvas: CustomPaint y CustomPainter . El segundo implementa su algoritmo de renderizado.

Lea m谩s aqu铆: StackOverflow

Un ejemplo:


 import 'package:flutter/material.dart'; void main() => runApp(MaterialApp(home: DemoApp())); class DemoApp extends StatelessWidget { Widget build(BuildContext context) => Scaffold(body: Signature()); } 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), ); } } 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; } 

Pregunta:


驴C贸mo crear widgets personalizados?

La respuesta es:


Componga widgets dentro de uno (en lugar de herencia).

Diferencias:


En Android, podemos heredar de la Vista que nos interesa y agregar nuestra propia l贸gica. En Flutter, esto es similar a un ViewGroup, solo el widget siempre se hereda de StatelessWidget o StatefulWidget. Es decir debe crear un nuevo widget y usarlo como el conjunto de widgets que necesita como par谩metros o campos.

Un ejemplo:


 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"), ); } 

Intenciones


Pregunta:


驴Cu谩l es el an谩logo de Intenci贸n en Flutter?

La respuesta es:


El no esta ahi. Para navegar entre pantallas, se utilizan las clases Navegador y Ruta .

Para interactuar con componentes externos (por ejemplo, una c谩mara o un selector de archivos), puede usar complementos o integraci贸n nativa en cada plataforma. Obtenga m谩s informaci贸n sobre la integraci贸n nativa: desarrollo de paquetes y complementos .

Diferencias:


Flutter no tiene tal cosa como actividad y fragmento. Hay Navigator (Navigator) y Rutas (rutas). La aplicaci贸n Flutter se asemeja a una aplicaci贸n de una sola actividad, donde diferentes pantallas representan diferentes fragmentos, y el FragmentManager los controla. Navigator es similar al FragmentManager en principio. Puede hacer push () o pop () a la ruta que especifique. La ruta es un tipo de fragmento, pero en Flutter es habitual compararlo con una pantalla o p谩gina.

En Android, describimos todas las actividades entre las que podemos navegar en AndroidManifest.xml.

Flutter tiene dos formas:

  • Describa un mapa llamado Ruta (MaterialApp)
  • Navega directamente a Ruta (WidgetApp).

Un ejemplo:


 void main() { runApp(MaterialApp( 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'); 

Pregunta:


驴C贸mo procesar intentos provenientes de otras aplicaciones?

La respuesta es:


Interactuando con la capa de Android de la aplicaci贸n a trav茅s de MethodChannel .

Un ejemplo:


Escribimos intent-filter en AndroidManifest.xml:

 <activity android:name=".MainActivity" android:launchMode="singleTop" android:theme="@style/LaunchTheme" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection" android:hardwareAccelerated="true" android:windowSoftInputMode="adjustResize"> <!-- ... --> <intent-filter> <action android:name="android.intent.action.SEND" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="text/plain" /> </intent-filter> </activity> 

Procesamos la intenci贸n en MainActivity y llamamos al c贸digo desde Flutter a trav茅s de MethodChannel:

 package com.example.shared; import android.content.Intent; import android.os.Bundle; import java.nio.ByteBuffer; import io.flutter.app.FlutterActivity; import io.flutter.plugin.common.ActivityLifecycleListener; import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; import io.flutter.plugins.GeneratedPluginRegistrant; public class MainActivity extends FlutterActivity { private String sharedText; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); GeneratedPluginRegistrant.registerWith(this); Intent intent = getIntent(); String action = intent.getAction(); String type = intent.getType(); if (Intent.ACTION_SEND.equals(action) && type != null) { if ("text/plain".equals(type)) { handleSendText(intent); // Handle text being sent } } new MethodChannel(getFlutterView(), "app.channel.shared.data").setMethodCallHandler( new MethodCallHandler() { @Override public void onMethodCall(MethodCall call, MethodChannel.Result result) { if (call.method.contentEquals("getSharedText")) { result.success(sharedText); sharedText = null; } } }); } void handleSendText(Intent intent) { sharedText = intent.getStringExtra(Intent.EXTRA_TEXT); } } 

Solicitamos datos cuando el widget comienza a dibujarse:

 import 'package:flutter/material.dart'; import 'package:flutter/services.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 Shared App Handler', 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> { static const platform = const MethodChannel('app.channel.shared.data'); String dataShared = "No data"; @override void initState() { super.initState(); getSharedText(); } @override Widget build(BuildContext context) { return Scaffold(body: Center(child: Text(dataShared))); } getSharedText() async { var sharedData = await platform.invokeMethod("getSharedText"); if (sharedData != null) { setState(() { dataShared = sharedData; }); } } } 

Pregunta:


驴Cu谩l es el an谩logo de startActivityForResult () ?

La respuesta es:


La palabra clave aguarda y el resultado de una clase Futuro .

Diferencias:


Despu茅s de llamar a startActivityForResult () en Android, necesitamos implementar el procesamiento en onActivityResult (). Flutter no necesita implementar nada, porque El m茅todo de navegador push () devuelve un objeto Future.

Un ejemplo:


 Map coordinates = await Navigator.of(context).pushNamed('/location'); 

Y cuando tengamos las coordenadas en la pantalla '/ ubicaci贸n', haga pop ():

 Navigator.of(context).pop({"lat":43.821757,"long":-79.226392}); 

IU as铆ncrona


Pregunta:


驴Cu谩l es el an谩logo de runOnUiThread () en Flutter?

La respuesta es:


Dart implementa un modelo de ejecuci贸n de subproceso 煤nico que se ejecuta en aislamientos . La ejecuci贸n asincr贸nica usa async / await, con la que puede estar familiarizado desde C #, JavaScript o las rutinas de Kotlin.

Un ejemplo:


Cumplir la solicitud y devolver el resultado para actualizar la interfaz de usuario:

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

Cuando se recibe la respuesta a la solicitud, debe llamar al m茅todo setState () para volver a dibujar el 谩rbol de widgets con los nuevos datos.

Un ejemplo:


Cargando y actualizando datos en un 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); }); } } 

Pregunta:


驴C贸mo ejecutar c贸digo en un hilo de fondo?

La respuesta es:


Como se mencion贸 anteriormente, utilizando async / await y aislamiento (Isolate).

Diferencias:


Fuera de la caja en Android, puede usar AsyncTask. Debe implementar onPreExecute () , doInBackground () , onPostExecute () en 茅l . En Flutter "fuera de la caja" solo necesita usar async / wait, Dart se encargar谩 del resto.

Un ejemplo:


Aqu铆 el m茅todo dataLoader () est谩 aislado. De forma aislada, puede ejecutar operaciones pesadas como analizar JSON grandes, cifrado, procesamiento de im谩genes, 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; } } 

Pregunta:


驴Cu谩l es el equivalente de Flutter de OkHttp ?

La respuesta es:


Flutter tiene su propio paquete HTTP .

Informacion adicional:


Hasta ahora, no todas las caracter铆sticas OkHttp se han implementado en el paquete HTTP, por lo que muchas de las caracter铆sticas que faltan se abstraen y puede implementarlas usted mismo seg煤n sea necesario.

Un ejemplo:


Para usar el paquete HTTP, agr茅guelo como una dependencia en pubspec.yaml:

 dependencies: ... http: ^0.11.3+16 

Para ejecutar la solicitud, llame en espera en la funci贸n asincr贸nica 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); }); } } 

Pregunta:


驴C贸mo mostrar el progreso?

La respuesta es:


Usando el widget ProgressIndicator .

Un ejemplo:


 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); }); } } 

Estructura del proyecto y recursos


Pregunta:


驴D贸nde almacenar recursos de diferentes resoluciones?

La respuesta es:


En activos.

Diferencias:


En Android, los recursos tienen una carpeta res y tienen activos. Flutter solo tiene activos. La carpeta de activos se puede ubicar en cualquier parte del proyecto, lo m谩s importante es escribir la ruta en el archivo pubspec.yaml.

Informacion adicional:


Comparaci贸n de los tama帽os de los recursos gr谩ficos en Android y Flutter.
Calificador de densidad de AndroidRelaci贸n de p铆xeles de aleteo
ldpi0.75x
mdpi1.0x
hdpi1.5x
xhdpi2.0x
xxhdpi3.0x
xxxhdpi4.0x
Flutter usa AssetManager o clases especializadas que comienzan con Asset para usar recursos en el c贸digo.

Un ejemplo:


AssetManager:

 val flutterAssetStream = assetManager.open("flutter_assets/assets/my_flutter_asset.png") 

Ubicaci贸n del 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 

Ruta en el archivo pubspec.yaml:

 assets: - images/my_icon.jpeg 

Usando AssetImage :

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

Usando el activo directamente:

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

Pregunta:


驴D贸nde almacenar cadenas? 驴C贸mo localizarlos?

La respuesta es:


Almacenar en campos est谩ticos. Localizar usando el paquete intl .

Un ejemplo:


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

Pregunta:


驴Cu谩l es el an谩logo del archivo gradle? 驴C贸mo agregar dependencias?

La respuesta es:


pubspec.yaml.

Informacion adicional:


Flutter delega el ensamblaje a los creadores nativos de Android e iOS. Vea una lista de todas las bibliotecas populares para Flutter en Pub .

Actividades y fragmentos


Pregunta:


驴Cu谩l es el equivalente de Actividad y Fragmento en Flutter?

La respuesta es:


Todo en Flutter son widgets. Los widgets juegan el papel de la actividad y los fragmentos para trabajar con la interfaz de usuario. Y el papel de la navegaci贸n, como se menciona en el p谩rrafo sobre navegaci贸n, es Navegador y Ruta.

Informacion adicional:


Flutter para desarrolladores de Android: c贸mo dise帽ar una IU de actividad en Flutter .

Pregunta:


驴C贸mo manejar los eventos del ciclo de vida?

La respuesta es:


Usando el m茅todo WidgetsBinding y didChangeAppLifecycleState () .

Informacion adicional:


Flutter usa FlutterActivity en c贸digo nativo, y el motor Flutter hace que los cambios de estado de procesamiento sean lo m谩s discretos posible. Pero si a煤n necesita hacer alg煤n trabajo dependiendo del estado, el ciclo de vida es ligeramente diferente:

  • inactivo: este m茅todo es solo en iOS, en Android no hay an谩logo;
  • en pausa: similar a onPause () en Android;
  • resumido: similar a onPostResume () en Android;
  • suspensi贸n: similar a onStop en Android, en iOS no hay an谩logo.

Esto se describe con m谩s detalle en la documentaci贸n de AppLifecycleStatus .

Un ejemplo:


 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())); } 

Dise帽os


Pregunta:


驴Cu谩l es el an谩logo de LinearLayout ?

La respuesta es:


Fila para horizontal, columna para vertical.

Informacion adicional:


Flutter para desarrolladores de Android: 驴C贸mo dise帽ar LinearLayout en Flutter?

Un ejemplo:


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

Pregunta:


驴Cu谩l es el equivalente de RelativeLayout ?

La respuesta es:


Pila de widgets.

M谩s detalles:

Stackoverflow

Pregunta:


驴Cu谩l es el an谩logo de ScrollView ?

La respuesta es:


ListView con widgets.

Un ejemplo:


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

Pregunta:


驴C贸mo manejar las transiciones entre retrato y paisaje?

La respuesta es:


FlutterView maneja los flips si AndroidManifest.xml contiene
android: configChanges = "orientaci贸n | tama帽o de pantalla"

Gestos y manejo de eventos t谩ctiles


Pregunta:


驴C贸mo agregar onClick listener para widget en Flutter?

La respuesta es:


Si el widget admite clics, entonces en onPressed (). Si no, entonces en onTap ().

Un ejemplo:


En onPressed ():

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

En onTap ():

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

Pregunta:


驴C贸mo manejar otros gestos en widgets?

La respuesta es:


Usando el GestureDetector . Pueden manejar las siguientes acciones:

Toque



Doble toque



Pulsaci贸n larga



Arrastre vertical



Arrastre horizontal



Un ejemplo:


Procesamiento en DoubleTap:

 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(); } }, ), )); } } 

Vistas de lista y adaptadores


Pregunta:


驴Cu谩l es el an谩logo de ListView en Flutter?

La respuesta es:


ListView

Diferencias:


Flutter no tiene que pensar en limpiar y reutilizar elementos (que es lo que hace ListView / RecyclerView en Android, usando el patr贸n ViewHolder).

Un ejemplo:


 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; } } 

Pregunta:


驴C贸mo averiguar en qu茅 elemento se hizo clic?

La respuesta es:


Envolver un elemento en un GestureDetector .

Un ejemplo:


 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(GestureDetector( child: Padding( padding: EdgeInsets.all(10.0), child: Text("Row $i")), onTap: () { print('row tapped'); }, )); } return widgets; } } 

Pregunta:


驴C贸mo actualizar din谩micamente un ListView ?

La respuesta es:


Si tiene un peque帽o conjunto de datos, esto se puede hacer a trav茅s de setState () . Si el conjunto de datos es grande, entonces a trav茅s de ListView.Builder , que es un an谩logo de RecyclerView .

Un ejemplo:


Usando setState () :

 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 = <Widget>[]; @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'); }); }, ); } } 

Usando ListView.Builder :

 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 = <Widget>[]; @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'); }); }, ); } } 

Trabajar con texto


Pregunta:


驴C贸mo usar fuentes personalizadas?

La respuesta es:


Solo necesita poner el archivo de fuente en la carpeta (piense en el nombre usted mismo) e indique la ruta en pubspec.yaml.

Un ejemplo:


 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'), ), ), ); } 

Pregunta:


驴C贸mo dise帽ar widgets de texto?

La respuesta es:


Usando par谩metros:

  • color
  • decoracion
  • decorationColor;
  • decorationStyle;
  • fontFamily;
  • fontSize;
  • fontStyle;
  • fontWeight;
  • hashCode;
  • height;
  • inherit;
  • letterSpacing;
  • textBaseline;
  • wordSpacing.


: Retrieve the value of a text field .

:


hint TextInput ?

La respuesta es:


InputDecoration , .

Un ejemplo:


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

:


?

La respuesta es:


InputDecoration .

Un ejemplo:


 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> { 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 em) { 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(em); } } 

Flutter


:


GPS?

La respuesta es:


geolocator .

:


?

La respuesta es:


image_picker .

:


Facebook?

La respuesta es:


flutter_facebook_login .

:


Firebase?

La respuesta es:


Firebase Flutter first party plugins .


:


() ?

La respuesta es:


Flutter EventBus . : developing packages and plugins .

:


NDK?

La respuesta es:


NDK- Flutter. Flutter .

Themes


:


(Theme) ?

La respuesta es:


MaterialApp WidgetApp .

Un ejemplo:


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


:


Shared Preferences?

La respuesta es:


Shared_Preferences plugin ( NSUserDefaults iOS ).

Un ejemplo:


 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); } 

:


SQLite Flutter?

La respuesta es:


SQFlite .

Notificaciones


:


push-?

La respuesta es:


Firebase_Messaging .

Conclusi贸n


. , , . 芦 禄 . 芦-禄 . , ? 2016 Kotlin, - 2017. , , . , .
2016 Flutter Dart. , 2018 . . ! , , , . ( Google Fuchsia , , , Flutter ). 鈥 ! , 鈥 . Eso es todo para m铆. Google Play!

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


All Articles